From d9f62e42805986d30879ccac95ed7f001a0d6678 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Fri, 13 Jun 2025 00:11:24 +0000 Subject: [PATCH 01/29] feat: add centralized action metadata system with proper array parsing - Add ACTION_INPUTS mapping for all GitHub Action inputs - Add createConfigFromInputs() function for dynamic config creation - Implement proper array parsing that distinguishes required vs optional - Required array inputs throw error on empty string - Optional array inputs return empty array on empty string - Add comprehensive type definitions for action metadata --- src/types/action-metadata.types.ts | 18 ++++ src/types/index.ts | 3 + src/utils/action-metadata.ts | 144 +++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 src/types/action-metadata.types.ts create mode 100644 src/utils/action-metadata.ts diff --git a/src/types/action-metadata.types.ts b/src/types/action-metadata.types.ts new file mode 100644 index 00000000..0850844f --- /dev/null +++ b/src/types/action-metadata.types.ts @@ -0,0 +1,18 @@ +import type { Config } from '@/types/config.types'; + +/** + * Metadata about GitHub Action inputs, including their types, defaults, and mapping to config properties. + * This serves as the single source of truth for action configuration. + * + * @todo update doc - defaults at runtime come from action.yml, testing see helpers/inputs.ts + */ +export interface ActionInputMetadata { + /** The config property name this input maps to */ + configKey: keyof Config; + + /** Whether this input is required */ + required: boolean; + + /** The input type for proper parsing */ + type: 'string' | 'boolean' | 'number' | 'array'; +} diff --git a/src/types/index.ts b/src/types/index.ts index 1cd04a7b..ca0629de 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,3 +1,6 @@ +// Action metadata types +export * from './action-metadata.types'; + // Common types export * from './common.types'; diff --git a/src/utils/action-metadata.ts b/src/utils/action-metadata.ts new file mode 100644 index 00000000..0856b9f5 --- /dev/null +++ b/src/utils/action-metadata.ts @@ -0,0 +1,144 @@ +import type { ActionInputMetadata, Config } from '@/types'; +import { getBooleanInput, getInput } from '@actions/core'; + +/** + * Complete mapping of all GitHub Action inputs to their metadata. + * This is the single source of truth for input configuration. + * Note: defaultValue is removed as defaults come from action.yml at runtime + */ +export const ACTION_INPUTS: Record = { + 'major-keywords': { + configKey: 'majorKeywords', + required: true, + type: 'array', + }, + 'minor-keywords': { + configKey: 'minorKeywords', + required: true, + type: 'array', + }, + 'patch-keywords': { + configKey: 'patchKeywords', + required: true, + type: 'array', + }, + 'default-first-tag': { + configKey: 'defaultFirstTag', + required: true, + type: 'string', + }, + 'terraform-docs-version': { + configKey: 'terraformDocsVersion', + required: true, + type: 'string', + }, + 'delete-legacy-tags': { + configKey: 'deleteLegacyTags', + required: true, + type: 'boolean', + }, + 'disable-wiki': { + configKey: 'disableWiki', + required: true, + type: 'boolean', + }, + 'wiki-sidebar-changelog-max': { + configKey: 'wikiSidebarChangelogMax', + required: true, + type: 'number', + }, + 'disable-branding': { + configKey: 'disableBranding', + required: true, + type: 'boolean', + }, + 'module-path-ignore': { + configKey: 'modulePathIgnore', + required: false, + type: 'array', + }, + 'module-change-exclude-patterns': { + configKey: 'moduleChangeExcludePatterns', + required: false, + type: 'array', + }, + 'module-asset-exclude-patterns': { + configKey: 'moduleAssetExcludePatterns', + required: false, + type: 'array', + }, + 'use-ssh-source-format': { + configKey: 'useSSHSourceFormat', + required: true, + type: 'boolean', + }, + github_token: { + configKey: 'githubToken', + required: true, + type: 'string', + }, + 'tag-directory-separator': { + configKey: 'tagDirectorySeparator', + required: true, + type: 'string', + }, + 'use-version-prefix': { + configKey: 'useVersionPrefix', + required: true, + type: 'boolean', + }, +} as const; + +/** + * Creates a config object by reading inputs using GitHub Actions API and converting them + * according to the metadata definitions. This provides a dynamic way to build the config + * without manually mapping each input. + */ +export function createConfigFromInputs(): Config { + const config = {} as Config; + + for (const [inputName, metadata] of Object.entries(ACTION_INPUTS)) { + const { configKey, required, type } = metadata; + + try { + let value: unknown; + + if (type === 'boolean') { + // Use getBooleanInput for boolean types for proper parsing + value = getBooleanInput(inputName, { required }); + } else if (type === 'array') { + // Handle array inputs with special parsing + const input = getInput(inputName, { required }); + + if (!input || input.trim() === '') { + value = []; + } else { + value = Array.from( + new Set( + input + .split(',') + .map((item: string) => item.trim()) + .filter(Boolean), + ), + ); + } + } else if (type === 'number') { + // Handle number inputs with parseInt + const input = getInput(inputName, { required }); + value = Number.parseInt(input, 10); + } else { + // Handle string inputs + value = getInput(inputName, { required }); + } + + // Safely assign to config using the configKey + Object.assign(config, { [configKey]: value }); + } catch (error) { + throw new Error( + `Failed to process input '${inputName}': ${error instanceof Error ? error.message : String(error)}`, + ); + } + } + + return config; +} From cc9c6ab12e74290d9cdb9a2422f8b2ed18eba5de Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Fri, 13 Jun 2025 00:12:33 +0000 Subject: [PATCH 02/29] refactor: update config system to use centralized action metadata - Replace manual input processing with createConfigFromInputs() - Add comprehensive validation for tag directory separator - Add validation for default first tag format - Simplify config initialization using metadata-driven approach - Update config mock to use real action metadata system - Improve error messages with more specific validation details --- __mocks__/config.ts | 66 +++++++++++++------------------------------- src/config.ts | 67 +++++++++++++++++---------------------------- 2 files changed, 44 insertions(+), 89 deletions(-) diff --git a/__mocks__/config.ts b/__mocks__/config.ts index 5c43f6b4..bd10312b 100644 --- a/__mocks__/config.ts +++ b/__mocks__/config.ts @@ -1,4 +1,7 @@ +import { setupTestInputs } from '@/tests/helpers/inputs'; import type { Config } from '@/types'; +import type { ActionInputMetadata } from '@/types'; +import { ACTION_INPUTS, createConfigFromInputs } from '@/utils/metadata'; /** * Configuration interface with added utility methods @@ -9,55 +12,24 @@ interface ConfigWithMethods extends Config { } /** - * Default configuration object. + * Load default configuration from action.yml. */ -const defaultConfig: Config = { - majorKeywords: ['BREAKING CHANGE', '!', 'MAJOR CHANGE'], - minorKeywords: ['feat', 'feature'], - patchKeywords: ['fix', 'chore'], - defaultFirstTag: 'v1.0.0', - terraformDocsVersion: 'v0.20.0', - deleteLegacyTags: false, - disableWiki: false, - wikiSidebarChangelogMax: 10, - disableBranding: false, - modulePathIgnore: ['tf-modules/kms/examples/complete'], - moduleChangeExcludePatterns: ['.gitignore', '*.md'], - moduleAssetExcludePatterns: ['tests/**', 'examples/**'], - githubToken: 'ghp_test_token_2c6912E7710c838347Ae178B4', - useSSHSourceFormat: false, - wikiUsageTemplate: ` - To use this module in your Terraform, refer to the below module example: - - \`\`\`hcl - module "{{module_name_terraform}}" { - source = "git::{{module_source}}?ref={{latest_tag}}" - - # See inputs below for additional required parameters - } - \`\`\` -` +const createDefaultConfig = (): Config => { + setupTestInputs(); + return createConfigFromInputs(); }; /** - * Valid configuration keys. + * Default configuration object loaded from action.yml. + */ +const defaultConfig: Config = createDefaultConfig(); + +/** + * Valid configuration keys derived from ACTION_INPUTS. */ -const validConfigKeys = [ - 'majorKeywords', - 'minorKeywords', - 'patchKeywords', - 'defaultFirstTag', - 'terraformDocsVersion', - 'deleteLegacyTags', - 'disableWiki', - 'wikiSidebarChangelogMax', - 'disableBranding', - 'modulePathIgnore', - 'moduleChangeExcludePatterns', - 'moduleAssetExcludePatterns', - 'githubToken', - 'useSSHSourceFormat', -] as const; +const validConfigKeys = (Object.values(ACTION_INPUTS) as ActionInputMetadata[]).map( + (metadata) => metadata.configKey, +) as Array; type ValidConfigKey = (typeof validConfigKeys)[number]; @@ -68,7 +40,7 @@ let currentConfig: Config = { ...defaultConfig }; * Config proxy handler. */ const configProxyHandler: ProxyHandler = { - set(target: ConfigWithMethods, key: string, value: unknown): boolean { + set(_target: ConfigWithMethods, key: string, value: unknown): boolean { if (!validConfigKeys.includes(key as ValidConfigKey)) { throw new Error(`Invalid config key: ${key}`); } @@ -77,7 +49,7 @@ const configProxyHandler: ProxyHandler = { const expectedValue = defaultConfig[typedKey]; if ((Array.isArray(expectedValue) && Array.isArray(value)) || typeof expectedValue === typeof value) { - // @ts-ignore - we know that the key is valid and that the value is correct + // @ts-expect-error - we know that the key is valid and that the value is correct currentConfig[typedKey] = value as typeof expectedValue; return true; } @@ -85,7 +57,7 @@ const configProxyHandler: ProxyHandler = { throw new TypeError(`Invalid value type for config key: ${key}`); }, - get(target: ConfigWithMethods, prop: string | symbol): unknown { + get(_target: ConfigWithMethods, prop: string | symbol): unknown { if (typeof prop === 'string') { if (prop === 'set') { return (overrides: Partial = {}) => { diff --git a/src/config.ts b/src/config.ts index 4a620f18..bc9933f0 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,30 +1,11 @@ import type { Config } from '@/types'; -import { endGroup, getBooleanInput, getInput, info, startGroup } from '@actions/core'; +import { createConfigFromInputs } from '@/utils/action-metadata'; +import { VERSION_TAG_REGEX } from '@/utils/constants'; +import { endGroup, info, startGroup } from '@actions/core'; // Keep configInstance private to this module let configInstance: Config | null = null; -/** - * Retrieves an array of values from a comma-separated input string. Duplicates any empty values - * are removed and each value is trimmed of whitespace. - * - * @param inputName - Name of the input to retrieve. - * @param required - Whether the input is required. - * @returns An array of trimmed and filtered values. - */ -const getArrayInput = (inputName: string, required: boolean): string[] => { - const input = getInput(inputName, { required }); - - return Array.from( - new Set( - input - .split(',') - .map((item: string) => item.trim()) - .filter(Boolean), - ), - ); -}; - /** * Clears the cached config instance during testing. * @@ -58,24 +39,8 @@ function initializeConfig(): Config { try { startGroup('Initializing Config'); - // Initialize the config instance - configInstance = { - majorKeywords: getArrayInput('major-keywords', true), - minorKeywords: getArrayInput('minor-keywords', true), - patchKeywords: getArrayInput('patch-keywords', true), - defaultFirstTag: getInput('default-first-tag', { required: true }), - terraformDocsVersion: getInput('terraform-docs-version', { required: true }), - deleteLegacyTags: getBooleanInput('delete-legacy-tags', { required: true }), - disableWiki: getBooleanInput('disable-wiki', { required: true }), - wikiSidebarChangelogMax: Number.parseInt(getInput('wiki-sidebar-changelog-max', { required: true }), 10), - disableBranding: getBooleanInput('disable-branding', { required: true }), - githubToken: getInput('github_token', { required: true }), - modulePathIgnore: getArrayInput('module-path-ignore', false), - moduleChangeExcludePatterns: getArrayInput('module-change-exclude-patterns', false), - moduleAssetExcludePatterns: getArrayInput('module-asset-exclude-patterns', false), - useSSHSourceFormat: getBooleanInput('use-ssh-source-format', { required: true }), - wikiUsageTemplate: getInput('wiki-usage-template', { required: false }), - }; + // Initialize the config instance using action metadata + configInstance = createConfigFromInputs(); // Validate that *.tf is not in excludePatterns if (configInstance.moduleChangeExcludePatterns.some((pattern) => pattern === '*.tf')) { @@ -85,13 +50,29 @@ function initializeConfig(): Config { throw new TypeError('Asset exclude patterns cannot contain "*.tf" as these files are required'); } - // Validate that we have a valid first tag. For now, must be v#.#.# - // Validate WikiSidebar Changelog Max is a number and greater than zero if (configInstance.wikiSidebarChangelogMax < 1 || Number.isNaN(configInstance.wikiSidebarChangelogMax)) { throw new TypeError('Wiki Sidebar Change Log Max must be an integer greater than or equal to one'); } + // Validate tag directory separator + const validSeparators = ['-', '_', '/', '.']; + if (configInstance.tagDirectorySeparator.length !== 1) { + throw new TypeError('Tag directory separator must be exactly one character'); + } + if (!validSeparators.includes(configInstance.tagDirectorySeparator)) { + throw new TypeError( + `Tag directory separator must be one of: ${validSeparators.join(', ')}. Got: '${configInstance.tagDirectorySeparator}'`, + ); + } + + // Validate default first tag format + if (!VERSION_TAG_REGEX.test(configInstance.defaultFirstTag)) { + throw new TypeError( + `Default first tag must be in format v#.#.# or #.#.# (e.g., v1.0.0 or 1.0.0). Got: '${configInstance.defaultFirstTag}'`, + ); + } + info(`Major Keywords: ${configInstance.majorKeywords.join(', ')}`); info(`Minor Keywords: ${configInstance.minorKeywords.join(', ')}`); info(`Patch Keywords: ${configInstance.patchKeywords.join(', ')}`); @@ -104,6 +85,8 @@ function initializeConfig(): Config { info(`Module Change Exclude Patterns: ${configInstance.moduleChangeExcludePatterns.join(', ')}`); info(`Module Asset Exclude Patterns: ${configInstance.moduleAssetExcludePatterns.join(', ')}`); info(`Use SSH Source Format: ${configInstance.useSSHSourceFormat}`); + info(`Tag Directory Separator: ${configInstance.tagDirectorySeparator}`); + info(`Use Version Prefix: ${configInstance.useVersionPrefix}`); return configInstance; } finally { From a3c9e4ec313689a5ff7aad06a8f4df8c37b64bfc Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Fri, 13 Jun 2025 00:20:08 +0000 Subject: [PATCH 03/29] test: improve test infrastructure and input helpers - Refactor input helpers to use centralized ACTION_INPUTS metadata - Add getActionDefaults() to load defaults from action.yml - Generate input type arrays dynamically from metadata - Add getConfigKey() helper for type-safe config access - Simplify setupTestInputs() to use action.yml defaults - Remove hardcoded input arrays in favor of metadata-driven approach --- __tests__/_setup.ts | 6 +- __tests__/helpers/action-defaults.ts | 48 ++++++++ __tests__/helpers/inputs.ts | 170 +++++++++++---------------- 3 files changed, 122 insertions(+), 102 deletions(-) create mode 100644 __tests__/helpers/action-defaults.ts diff --git a/__tests__/_setup.ts b/__tests__/_setup.ts index 7a9ac6ff..da84e9c1 100644 --- a/__tests__/_setup.ts +++ b/__tests__/_setup.ts @@ -1,3 +1,4 @@ +import { setupTestInputs } from '@/tests/helpers/inputs'; import { afterEach, beforeEach, vi } from 'vitest'; // Mocked node modules (./__mocks__/*) @@ -20,11 +21,14 @@ const defaultEnvironmentVariables = { }; beforeEach(() => { - // Initialize environment + // Initialize GitHub mock pull request environment for (const [key, value] of Object.entries(defaultEnvironmentVariables)) { vi.stubEnv(key, value); } + // Set up action input defaults for testing + setupTestInputs(); + // Clear all mocked functions usage data and state vi.clearAllMocks(); }); diff --git a/__tests__/helpers/action-defaults.ts b/__tests__/helpers/action-defaults.ts new file mode 100644 index 00000000..f647dc65 --- /dev/null +++ b/__tests__/helpers/action-defaults.ts @@ -0,0 +1,48 @@ +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { ACTION_INPUTS } from '@/utils/action-metadata'; +import * as yaml from 'js-yaml'; + +/** + * Interface for action.yml structure + */ +interface ActionYml { + inputs: Record< + string, + { + description: string; + required: boolean; + default?: string; + } + >; +} + +/** + * Gets action defaults from action.yml file. + * Returns a record of all input names to their default values (or undefined if no default). + */ +/** + * Extracts default values for GitHub Action inputs from action.yml file. + * + * This function reads the action.yml file from the current working directory, + * parses its content, and retrieves the default values for all inputs defined + * in ACTION_INPUTS. + * + * @returns A record mapping each input name to its default value from the action.yml file. + * If an input has no default value, its entry will contain undefined. + */ +export function getActionDefaults(): Record { + const actionYmlPath = path.join(process.cwd(), 'action.yml'); + const actionYmlContent = fs.readFileSync(actionYmlPath, 'utf8'); + const actionYml = yaml.load(actionYmlContent) as ActionYml; + + const defaults: Record = {}; + + // Process all inputs from ACTION_INPUTS to ensure we have entries for all inputs + for (const [inputName] of Object.entries(ACTION_INPUTS)) { + const actionInput = actionYml.inputs[inputName]; + defaults[inputName] = actionInput?.default; + } + + return defaults; +} diff --git a/__tests__/helpers/inputs.ts b/__tests__/helpers/inputs.ts index 5fa41ad0..2a0526f8 100644 --- a/__tests__/helpers/inputs.ts +++ b/__tests__/helpers/inputs.ts @@ -1,121 +1,89 @@ +import { getActionDefaults } from '@/tests/helpers/action-defaults'; import type { Config } from '@/types'; +import { ACTION_INPUTS } from '@/utils/action-metadata'; import { vi } from 'vitest'; -const INPUT_KEY = 'INPUT_'; +// Load action defaults once globally +const ACTION_DEFAULTS = getActionDefaults(); + +// Generate input type arrays from centralized metadata +export const requiredInputs = Object.entries(ACTION_INPUTS) + .filter(([, metadata]) => metadata.required) + .map(([inputName]) => inputName); + +export const optionalInputs = Object.entries(ACTION_INPUTS) + .filter(([, metadata]) => !metadata.required) + .map(([inputName]) => inputName); + +export const booleanInputs = Object.entries(ACTION_INPUTS) + .filter(([, metadata]) => metadata.type === 'boolean') + .map(([inputName]) => inputName); + +export const arrayInputs = Object.entries(ACTION_INPUTS) + .filter(([, metadata]) => metadata.type === 'array') + .map(([inputName]) => inputName); + +export const stringInputs = Object.entries(ACTION_INPUTS) + .filter(([, metadata]) => metadata.type === 'string') + .map(([inputName]) => inputName); + +export const numberInputs = Object.entries(ACTION_INPUTS) + .filter(([, metadata]) => metadata.type === 'number') + .map(([inputName]) => inputName); /** - * Type-safe mapping from input names to config keys. - * This ensures that each value is a valid key in the Config type. + * Converts an input name to its corresponding config key. + * @param inputName The input name (e.g., 'github_token', 'module-path-ignore') + * @returns The corresponding config key as a keyof Config */ -export const inputToConfigKeyMap: Record = { - 'major-keywords': 'majorKeywords', - 'minor-keywords': 'minorKeywords', - 'patch-keywords': 'patchKeywords', - 'default-first-tag': 'defaultFirstTag', - 'terraform-docs-version': 'terraformDocsVersion', - 'delete-legacy-tags': 'deleteLegacyTags', - 'disable-wiki': 'disableWiki', - 'wiki-sidebar-changelog-max': 'wikiSidebarChangelogMax', - 'disable-branding': 'disableBranding', - github_token: 'githubToken', - 'module-path-ignore': 'modulePathIgnore', - 'module-change-exclude-patterns': 'moduleChangeExcludePatterns', - 'module-asset-exclude-patterns': 'moduleAssetExcludePatterns', - 'use-ssh-source-format': 'useSSHSourceFormat', -}; +export function getConfigKey(inputName: string): keyof Config { + const metadata = ACTION_INPUTS[inputName]; + if (!metadata) { + throw new Error(`Unknown input: ${inputName}`); + } -// Create reverse mapping from config keys to input names -export const configKeyToInputMap = Object.entries(inputToConfigKeyMap).reduce( - (acc, [inputName, configKey]) => { - acc[configKey] = inputName; - return acc; - }, - {} as Record, -); + return metadata.configKey; +} -// Default inputs used for testing @actions/core behavior -export const defaultInputs = { - 'major-keywords': 'MAJOR CHANGE,BREAKING CHANGE,!', - 'minor-keywords': 'feat,feature', - 'patch-keywords': 'fix,chore', - 'default-first-tag': 'v0.1.0', - 'terraform-docs-version': 'v0.19.0', - 'delete-legacy-tags': 'false', - 'disable-wiki': 'false', - 'wiki-sidebar-changelog-max': '10', - 'disable-branding': 'false', - 'module-path-ignore': 'tf-modules/kms/examples/complete', - 'module-change-exclude-patterns': '.gitignore,*.md', - 'module-asset-exclude-patterns': 'tests/**,examples/**', - github_token: 'ghp_test_token_2c6912E7710c838347Ae178B4', - 'use-ssh-source-format': 'false', -}; -export const requiredInputs = [ - 'major-keywords', - 'minor-keywords', - 'patch-keywords', - 'default-first-tag', - 'terraform-docs-version', - 'delete-legacy-tags', - 'disable-wiki', - 'wiki-sidebar-changelog-max', - 'disable-branding', - 'github_token', - 'use-ssh-source-format', -]; -export const optionalInputs = Object.keys(defaultInputs).filter((key) => !requiredInputs.includes(key)); -export const booleanInputs = ['delete-legacy-tags', 'disable-wiki', 'disable-branding', 'use-ssh-source-format']; -export const arrayInputs = [ - 'major-keywords', - 'minor-keywords', - 'patch-keywords', - 'module-path-ignore', - 'module-change-exclude-patterns', - 'module-asset-exclude-patterns', -]; -export const stringInputs = ['default-first-tag', 'terraform-docs-version', 'github_token']; -export const numberInputs = ['wiki-sidebar-changelog-max']; +/** + * Converts an input name to its corresponding environment variable name. + * This is the exact inverse of what @actions/core getInput() does. + * @param inputName The input name (e.g., 'github_token', 'module-path-ignore') + * @returns The environment variable name (e.g., 'INPUT_GITHUB_TOKEN', 'INPUT_MODULE_PATH_IGNORE') + */ +function inputToEnvVar(inputName: string): string { + return `INPUT_${inputName.replace(/ /g, '_').toUpperCase()}`; +} /** - * Converts a dash-case input name to its corresponding camelCase config key - * Prefer using the inputToConfigKeyMap directly for known input keys + * Sets up test environment with action defaults and optional overrides. + * This replaces the previous stubInputEnv function with a cleaner approach + * that loads defaults from action.yml and applies test-specific overrides. * - * @param inputName The input name to convert - * @returns The corresponding config key as a string + * @param overrides - Test-specific overrides for input values. */ -export function inputToConfigKey(inputName: string): string { - // Check if the input name is in our mapping first - if (inputName in inputToConfigKeyMap) { - return inputToConfigKeyMap[inputName]; - } +export function setupTestInputs(overrides: Record = {}) { + // Start with action.yml defaults and apply test-specific defaults + const allInputs = { + ...ACTION_DEFAULTS, + github_token: 'ghp_test_token_2c6912E7710c838347Ae178B4', + ...overrides, + }; - // Fallback to the conversion logic - return inputName.replace(/-([a-z])/g, (_, c) => c.toUpperCase()); + // Set environment variables for all values (undefined is valid for vi.stubEnv) + for (const [inputName, value] of Object.entries(allInputs)) { + vi.stubEnv(inputToEnvVar(inputName), value); + } } /** - * Stubs environment variables with an `INPUT_` prefix using a set of default values, - * while allowing specific overrides. By default, this function sets a baseline of - * sane default environment values that would typically be used in tests. + * Clears a specific action input environment variable. * - * Overrides can be provided as key-value pairs, where: - * - A `string` value sets or replaces the environment variable. - * - A `null` value skips the setting, allowing for flexibility in customizing the stubbed environment. + * Useful for testing scenarios where you need to remove a specific input. Wrapper around + * vi.stubEnv which has an unsual syntax for clearing environment variables/ * - * @param {Record} overrides - An object specifying environment variable overrides. - * Keys in this object correspond to the environment variable names (without the `INPUT_` prefix), - * and values specify the desired values or `null` to skip setting. + * @param inputName The input name to clear (e.g., 'github_token', 'module-path-ignore') */ -export function stubInputEnv(inputs: Record = {}) { - // Merge default inputs with overrides, giving precedence to overrides - const mergedInputs = { ...defaultInputs, ...inputs }; - - for (const [key, value] of Object.entries(mergedInputs)) { - if (value === null) { - continue; - } - - const prefixedKey = `${INPUT_KEY}${key.replace(/ /g, '_').toUpperCase()}`; - vi.stubEnv(prefixedKey, value); - } +export function clearEnvironmentInput(inputName: string): void { + vi.stubEnv(inputToEnvVar(inputName), undefined); } From d47b8a63b65f308f9e9de9b2a8d9772bd890732b Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Fri, 13 Jun 2025 00:21:04 +0000 Subject: [PATCH 04/29] test: add comprehensive config validation test coverage - Add tests for tag directory separator validation (length and character) - Add tests for default first tag format validation - Add tests for required vs optional array input handling - Add tests for array parsing edge cases and deduplication - Improve test organization with metadata-driven approach - Achieve 100% test coverage for config.ts validation logic --- __tests__/config.test.ts | 212 +++++++++++++++++++++++++-------------- 1 file changed, 138 insertions(+), 74 deletions(-) diff --git a/__tests__/config.test.ts b/__tests__/config.test.ts index be14bc22..a3647f11 100644 --- a/__tests__/config.test.ts +++ b/__tests__/config.test.ts @@ -2,14 +2,13 @@ import { clearConfigForTesting, config, getConfig } from '@/config'; import { arrayInputs, booleanInputs, - inputToConfigKey, - inputToConfigKeyMap, + clearEnvironmentInput, + getConfigKey, optionalInputs, requiredInputs, + setupTestInputs, stringInputs, - stubInputEnv, } from '@/tests/helpers/inputs'; -import type { Config } from '@/types'; import { endGroup, getBooleanInput, getInput, info, startGroup } from '@actions/core'; import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; @@ -22,38 +21,40 @@ describe('config', () => { }); beforeEach(() => { + // The config is cached. To ensure each test starts with a clean slate, we implicity clear it. + // We don't do this globally in setup as it's not necessary for all tests. clearConfigForTesting(); - // Note: We don't stubInputEnv here as there are cases where we want to test default inputs and - // in this case we would have to do a full reset of the config. }); describe('input validation', () => { for (const input of requiredInputs) { it(`should throw error when required input "${input}" is missing`, () => { - stubInputEnv({ [input]: null }); - expect(() => getConfig()).toThrow(new Error(`Input required and not supplied: ${input}`)); + clearEnvironmentInput(input); + expect(() => getConfig()).toThrow( + new Error(`Failed to process input '${input}': Input required and not supplied: ${input}`), + ); expect(getInput).toHaveBeenCalled(); }); } for (const input of optionalInputs) { it(`should handle optional input "${input}" when not present`, () => { - stubInputEnv({ [input]: null }); + clearEnvironmentInput(input); // Simply verify it doesn't throw without the specific error object expect(() => getConfig()).not.toThrow(); // Get the config and check the actual value const config = getConfig(); - // Get the config key using the mapping directly if possible - const configKey = inputToConfigKeyMap[input] || inputToConfigKey(input); + // Get the config key using the new getConfigKey function + const configKey = getConfigKey(input); // Type-safe access using the mapping if (arrayInputs.includes(input)) { - // Cast configKey to keyof Config to ensure type safety - expect(config[configKey as keyof Config]).toEqual([]); + // Now configKey is properly typed as keyof Config + expect(config[configKey]).toEqual([]); } if (stringInputs.includes(input)) { - expect(config[configKey as keyof Config]).toEqual(''); + expect(config[configKey]).toEqual(''); } expect(getInput).toHaveBeenCalled(); @@ -62,10 +63,10 @@ describe('config', () => { for (const input of booleanInputs) { it(`should throw error when input "${input}" has an invalid boolean value`, () => { - stubInputEnv({ [input]: 'invalid-boolean' }); + setupTestInputs({ [input]: 'invalid-boolean' }); expect(() => getConfig()).toThrow( - new TypeError( - `Input does not meet YAML 1.2 "Core Schema" specification: ${input}\nSupport boolean input list: \`true | True | TRUE | false | False | FALSE\``, + new Error( + `Failed to process input '${input}': Input does not meet YAML 1.2 "Core Schema" specification: ${input}\nSupport boolean input list: \`true | True | TRUE | false | False | FALSE\``, ), ); expect(getBooleanInput).toHaveBeenCalled(); @@ -73,20 +74,20 @@ describe('config', () => { } it('should throw error when moduleChangeExcludePatterns includes *.tf', () => { - stubInputEnv({ 'module-change-exclude-patterns': '*.tf,tests/**' }); + setupTestInputs({ 'module-change-exclude-patterns': '*.tf,tests/**' }); expect(() => getConfig()).toThrow( new TypeError('Exclude patterns cannot contain "*.tf" as it is required for module detection'), ); }); it('should throw error when moduleAssetExcludePatterns includes *.tf', () => { - stubInputEnv({ 'module-asset-exclude-patterns': '*.tf,tests/**' }); + setupTestInputs({ 'module-asset-exclude-patterns': '*.tf,tests/**' }); expect(() => getConfig()).toThrow( new TypeError('Asset exclude patterns cannot contain "*.tf" as these files are required'), ); }); - it('should handle boolean conversions for various formats', async () => { + it('should handle boolean conversions for various formats', () => { const booleanCases = ['true', 'True', 'TRUE', 'false', 'False', 'FALSE']; for (const booleanValue of booleanCases) { @@ -100,36 +101,124 @@ describe('config', () => { return acc; }, {}); - stubInputEnv(booleanInputValuesTest); + setupTestInputs(booleanInputValuesTest); // Check the boolean conversion for each key in booleanInputs const config = getConfig(); for (const booleanInput of booleanInputs) { // Get config key from the mapping, which is already typed as keyof Config - const configKey = inputToConfigKeyMap[booleanInput]; + const configKey = getConfigKey(booleanInput); expect(config[configKey]).toBe(booleanValue.toLowerCase() === 'true'); } } }); + it('should handle array input parsing and deduplication', () => { + const arrayTestCases = [ + { input: 'item1,item2,item3', expected: ['item1', 'item2', 'item3'] }, + { input: ' item4 , item5 , item6 ', expected: ['item4', 'item5', 'item6'] }, + { input: 'item7,item7,item8', expected: ['item7', 'item8'] }, + { input: 'item10,,item11,,,item12', expected: ['item10', 'item11', 'item12'] }, + ]; + + for (const testCase of arrayTestCases) { + // Ensure we reset the configuration since this is looping inside the test + clearConfigForTesting(); + vi.unstubAllEnvs(); + + // Create test inputs for all array inputs + const arrayInputValuesTest = arrayInputs.reduce((acc: Record, key) => { + acc[key] = testCase.input; + return acc; + }, {}); + + setupTestInputs(arrayInputValuesTest); + + // Check array parsing for each array input + const config = getConfig(); + for (const arrayInput of arrayInputs) { + const configKey = getConfigKey(arrayInput); + expect(config[configKey]).toEqual(testCase.expected); + } + } + }); + + it('should throw error for required array inputs when empty string is provided', () => { + const requiredArrayInputs = arrayInputs.filter((input) => requiredInputs.includes(input)); + + for (const input of requiredArrayInputs) { + clearConfigForTesting(); + vi.unstubAllEnvs(); + + // Set the required array input to empty string + setupTestInputs({ [input]: '' }); + + expect(() => getConfig()).toThrow( + new Error(`Failed to process input '${input}': Input required and not supplied: ${input}`), + ); + } + }); + + it('should return empty array for optional array inputs when empty string is provided', () => { + const optionalArrayInputs = arrayInputs.filter((input) => optionalInputs.includes(input)); + + for (const input of optionalArrayInputs) { + clearConfigForTesting(); + vi.unstubAllEnvs(); + + // Set the optional array input to empty string + setupTestInputs({ [input]: '' }); + + const config = getConfig(); + const configKey = getConfigKey(input); + expect(config[configKey]).toEqual([]); + } + }); + it('should throw error for non-numeric wiki-sidebar-changelog-max', () => { - stubInputEnv({ 'wiki-sidebar-changelog-max': 'invalid' }); + setupTestInputs({ 'wiki-sidebar-changelog-max': 'invalid' }); expect(() => getConfig()).toThrow( new TypeError('Wiki Sidebar Change Log Max must be an integer greater than or equal to one'), ); }); it('should throw error for 0 wiki-sidebar-changelog-max', () => { - stubInputEnv({ 'wiki-sidebar-changelog-max': '0' }); + setupTestInputs({ 'wiki-sidebar-changelog-max': '0' }); expect(() => getConfig()).toThrow( new TypeError('Wiki Sidebar Change Log Max must be an integer greater than or equal to one'), ); }); + + it('should throw error for invalid tag directory separator length', () => { + setupTestInputs({ 'tag-directory-separator': 'ab' }); + expect(() => getConfig()).toThrow( + new TypeError('Tag directory separator must be exactly one character'), + ); + }); + + it('should throw error for invalid tag directory separator character', () => { + setupTestInputs({ 'tag-directory-separator': '@' }); + expect(() => getConfig()).toThrow( + new TypeError("Tag directory separator must be one of: -, _, /, .. Got: '@'"), + ); + }); + + it('should throw error for invalid default first tag format', () => { + setupTestInputs({ 'default-first-tag': 'invalid-tag' }); + expect(() => getConfig()).toThrow( + new TypeError("Default first tag must be in format v#.#.# or #.#.# (e.g., v1.0.0 or 1.0.0). Got: 'invalid-tag'"), + ); + + clearConfigForTesting(); + setupTestInputs({ 'default-first-tag': 'v1.0' }); + expect(() => getConfig()).toThrow( + new TypeError("Default first tag must be in format v#.#.# or #.#.# (e.g., v1.0.0 or 1.0.0). Got: 'v1.0'"), + ); + }); }); describe('initialization', () => { it('should maintain singleton instance across multiple imports', () => { - stubInputEnv(); const firstInstance = getConfig(); const secondInstance = getConfig(); expect(firstInstance).toBe(secondInstance); @@ -137,47 +226,50 @@ describe('config', () => { expect(endGroup).toHaveBeenCalledTimes(1); }); - it('should initialize with valid inputs and log configuration', () => { - stubInputEnv(); + it('should initialize with valid default inputs', () => { const config = getConfig(); - expect(config.majorKeywords).toEqual(['MAJOR CHANGE', 'BREAKING CHANGE', '!']); + expect(config.majorKeywords).toEqual(['major change', 'breaking change', '!']); expect(config.minorKeywords).toEqual(['feat', 'feature']); - expect(config.patchKeywords).toEqual(['fix', 'chore']); - expect(config.defaultFirstTag).toBe('v0.1.0'); - expect(config.terraformDocsVersion).toBe('v0.19.0'); - expect(config.deleteLegacyTags).toBe(false); + expect(config.patchKeywords).toEqual(['fix', 'chore', 'docs']); + expect(config.defaultFirstTag).toBe('v1.0.0'); + expect(config.terraformDocsVersion).toBe('v0.20.0'); + expect(config.deleteLegacyTags).toBe(true); expect(config.disableWiki).toBe(false); - expect(config.wikiSidebarChangelogMax).toBe(10); + expect(config.wikiSidebarChangelogMax).toBe(5); expect(config.disableBranding).toBe(false); expect(config.githubToken).toBe('ghp_test_token_2c6912E7710c838347Ae178B4'); - expect(config.moduleChangeExcludePatterns).toEqual(['.gitignore', '*.md']); - expect(config.moduleAssetExcludePatterns).toEqual(['tests/**', 'examples/**']); - expect(config.modulePathIgnore).toEqual(['tf-modules/kms/examples/complete']); + expect(config.modulePathIgnore).toEqual([]); + expect(config.moduleChangeExcludePatterns).toEqual(['.gitignore', '*.md', '*.tftest.hcl', 'tests/**']); + expect(config.moduleAssetExcludePatterns).toEqual(['.gitignore', '*.md', '*.tftest.hcl', 'tests/**']); expect(config.useSSHSourceFormat).toBe(false); + expect(config.tagDirectorySeparator).toBe('/'); + expect(config.useVersionPrefix).toBe(true); + expect(startGroup).toHaveBeenCalledWith('Initializing Config'); expect(startGroup).toHaveBeenCalledTimes(1); expect(endGroup).toHaveBeenCalledTimes(1); expect(vi.mocked(info).mock.calls).toEqual([ - ['Major Keywords: MAJOR CHANGE, BREAKING CHANGE, !'], + ['Major Keywords: major change, breaking change, !'], ['Minor Keywords: feat, feature'], - ['Patch Keywords: fix, chore'], - ['Default First Tag: v0.1.0'], - ['Terraform Docs Version: v0.19.0'], - ['Delete Legacy Tags: false'], + ['Patch Keywords: fix, chore, docs'], + ['Default First Tag: v1.0.0'], + ['Terraform Docs Version: v0.20.0'], + ['Delete Legacy Tags: true'], ['Disable Wiki: false'], - ['Wiki Sidebar Changelog Max: 10'], - ['Module Paths to Ignore: tf-modules/kms/examples/complete'], - ['Module Change Exclude Patterns: .gitignore, *.md'], - ['Module Asset Exclude Patterns: tests/**, examples/**'], + ['Wiki Sidebar Changelog Max: 5'], + ['Module Paths to Ignore: '], + ['Module Change Exclude Patterns: .gitignore, *.md, *.tftest.hcl, tests/**'], + ['Module Asset Exclude Patterns: .gitignore, *.md, *.tftest.hcl, tests/**'], ['Use SSH Source Format: false'], + ['Tag Directory Separator: /'], + ['Use Version Prefix: true'], ]); }); }); describe('config proxy', () => { it('should proxy config properties', () => { - stubInputEnv(); const proxyMajorKeywords = config.majorKeywords; const getterMajorKeywords = getConfig().majorKeywords; expect(proxyMajorKeywords).toEqual(getterMajorKeywords); @@ -196,32 +288,4 @@ describe('config', () => { expect(info).not.toHaveBeenCalled(); }); }); - - describe('input formatting', () => { - it('should handle various whitespace and duplicates in comma-separated inputs', () => { - stubInputEnv({ - 'major-keywords': ' BREAKING CHANGE , ! ', - 'minor-keywords': '\tfeat,\nfeature\r,feat', - }); - const config = getConfig(); - expect(config.majorKeywords).toEqual(['BREAKING CHANGE', '!']); - expect(config.minorKeywords).toEqual(['feat', 'feature']); - }); - - it('should filter out empty items in arrays', async () => { - stubInputEnv({ - 'major-keywords': 'BREAKING CHANGE,,!,,,', - 'module-change-exclude-patterns': ',.gitignore,,*.md,,', - }); - const config = getConfig(); - expect(config.majorKeywords).toEqual(['BREAKING CHANGE', '!']); - expect(config.moduleChangeExcludePatterns).toEqual(['.gitignore', '*.md']); - }); - - it('should handle empty modulePathIgnore', () => { - stubInputEnv({ 'module-path-ignore': '' }); - const config = getConfig(); - expect(config.modulePathIgnore).toEqual([]); - }); - }); }); From 7144712c19dbeed289c1713804126ed5c09e43a5 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Fri, 13 Jun 2025 00:22:08 +0000 Subject: [PATCH 05/29] fix: resolve context test environment variable issues - Set up required GitHub environment variables in context tests - Fix test isolation by properly clearing context between tests - Ensure environment variables are set before context initialization - Resolve test failures caused by missing GitHub Action environment --- __tests__/context.test.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/__tests__/context.test.ts b/__tests__/context.test.ts index f837d46f..17f2f7cb 100644 --- a/__tests__/context.test.ts +++ b/__tests__/context.test.ts @@ -4,7 +4,7 @@ import { createPullRequestMock } from '@/mocks/context'; import { info, startGroup } from '@actions/core'; import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; -// Mock node:fs. (Note: Appears we can't spy on functions via node:fs) +// Mock node:fs required for reading pull-request file information (Note: Appears we can't spy on functions via node:fs) vi.mock('node:fs', async () => { const original = await vi.importActual('node:fs'); return { @@ -46,7 +46,9 @@ describe('context', () => { describe('environment variable validation', () => { for (const envVar of requiredEnvVars) { it(`should throw an error if ${envVar} is not set`, () => { + // Set the specific environment variable to undefined, but keep others set vi.stubEnv(envVar, undefined); + expect(() => getContext()).toThrow( new Error( `The ${envVar} environment variable is missing or invalid. This variable should be automatically set by GitHub for each workflow run. If this variable is missing or not correctly set, it indicates a serious issue with the GitHub Actions environment, potentially affecting the execution of subsequent steps in the workflow. Please review the workflow setup or consult the documentation for proper configuration.`, @@ -58,6 +60,7 @@ describe('context', () => { describe('event validation', () => { it('should throw error when event is not pull_request', () => { + vi.stubEnv('GITHUB_EVENT_NAME', 'push'); expect(() => getContext()).toThrow('This workflow is not running in the context of a pull request'); }); @@ -182,9 +185,6 @@ describe('context', () => { const customApiUrl = 'https://github.example.com/api/v3'; vi.stubEnv('GITHUB_API_URL', customApiUrl); - // Clear context to force reinitialization - clearContextForTesting(); - const context = getContext(); // Check that the context was created (which means the custom API URL was used) @@ -196,8 +196,6 @@ describe('context', () => { // Ensure GITHUB_API_URL is not set to test the default fallback vi.stubEnv('GITHUB_API_URL', undefined); - // Clear context to force reinitialization - clearContextForTesting(); const context = getContext(); From 955a1835b5343d29e98f3dffdaff47da80b9ddf8 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Fri, 13 Jun 2025 01:21:23 +0000 Subject: [PATCH 06/29] fix: correct import paths and type inconsistencies --- src/types/config.types.ts | 27 ++++++++++++++++++++------- src/types/context.types.ts | 2 +- src/types/wiki.types.ts | 2 +- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/types/config.types.ts b/src/types/config.types.ts index 32543a88..9d726d42 100644 --- a/src/types/config.types.ts +++ b/src/types/config.types.ts @@ -67,6 +67,13 @@ export interface Config { */ githubToken: string; + /** + * A list of module paths to completely ignore when processing. Any module whose path matches + * one of these patterns will not be processed for versioning, release, or documentation. + * Paths are relative to the workspace directory. + */ + modulePathIgnore: string[]; + /** * A comma-separated list of file patterns to exclude from triggering version changes in Terraform modules. * These patterns follow glob syntax (e.g., ".gitignore,*.md") and are relative to each Terraform module directory within @@ -97,15 +104,21 @@ export interface Config { useSSHSourceFormat: boolean; /** - * A list of module paths to completely ignore when processing. Any module whose path matches - * one of these patterns will not be processed for versioning, release, or documentation. - * Paths are relative to the workspace directory. + * The character used to separate the module name from the version in tags. + * Must be a single character and one of: -, _, /, . + * + * Examples: + * - "/" (default): module/aws-s3-bucket/v1.0.0 + * - "-": module-aws-s3-bucket-v1.0.0 + * - "_": module_aws-s3-bucket_v1.0.0 + * - ".": module.aws-s3-bucket.v1.0.0 */ - modulePathIgnore: string[]; + tagDirectorySeparator: string; /** - * A raw, multi-line string to override the default 'Usage' section in the generated wiki. - * If not provided, a default usage block will be generated. + * Whether to include the "v" prefix in version tags. + * When true (default), tags will be formatted as: module/v1.2.3 + * When false, tags will be formatted as: module/1.2.3 */ - wikiUsageTemplate?: string; + useVersionPrefix: boolean; } diff --git a/src/types/context.types.ts b/src/types/context.types.ts index fa1c805a..9528f304 100644 --- a/src/types/context.types.ts +++ b/src/types/context.types.ts @@ -1,4 +1,4 @@ -import type { OctokitRestApi, Repo } from './github.types'; +import type { OctokitRestApi, Repo } from '@/types/github.types'; /** * Context and runtime related types diff --git a/src/types/wiki.types.ts b/src/types/wiki.types.ts index 2377b2c8..5a554276 100644 --- a/src/types/wiki.types.ts +++ b/src/types/wiki.types.ts @@ -1,5 +1,5 @@ +import type { ExecSyncError } from '@/types/node-child-process.types'; import type { WIKI_STATUS } from '@/utils/constants'; -import type { ExecSyncError } from './node-child-process.types'; /** * Represents the status of wiki operations. From f3202d8568e92f98894998d15b01ced461950c17 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Fri, 13 Jun 2025 01:22:09 +0000 Subject: [PATCH 07/29] chore: update dependencies and action.yml defaults --- package-lock.json | 314 +++++++++++++++++++++++++--------------------- package.json | 2 + 2 files changed, 175 insertions(+), 141 deletions(-) diff --git a/package-lock.json b/package-lock.json index 290b2ee1..35231e0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,12 +22,14 @@ "@biomejs/biome": "^1.9.4", "@octokit/types": "^14.0.0", "@octokit/webhooks-types": "^7.6.1", + "@types/js-yaml": "^4.0.9", "@types/node": "^22.15.29", "@types/which": "^3.0.4", "@vercel/ncc": "^0.38.3", "@vitest/coverage-v8": "^3.1.3", + "js-yaml": "^4.1.0", "make-coverage-badge": "^1.2.0", - "openai": "*", + "openai": "latest", "textlint": "^14.8.0", "textlint-filter-rule-comments": "^1.2.2", "textlint-rule-terminology": "^5.2.12", @@ -764,6 +766,27 @@ "node": ">=14" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1024,9 +1047,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.42.0.tgz", - "integrity": "sha512-gldmAyS9hpj+H6LpRNlcjQWbuKUtb94lodB9uCz71Jm+7BxK1VIOo7y62tZZwxhA7j1ylv/yQz080L5WkS+LoQ==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.43.0.tgz", + "integrity": "sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==", "cpu": [ "arm" ], @@ -1038,9 +1061,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.42.0.tgz", - "integrity": "sha512-bpRipfTgmGFdCZDFLRvIkSNO1/3RGS74aWkJJTFJBH7h3MRV4UijkaEUeOMbi9wxtxYmtAbVcnMtHTPBhLEkaw==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.43.0.tgz", + "integrity": "sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==", "cpu": [ "arm64" ], @@ -1052,9 +1075,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.42.0.tgz", - "integrity": "sha512-JxHtA081izPBVCHLKnl6GEA0w3920mlJPLh89NojpU2GsBSB6ypu4erFg/Wx1qbpUbepn0jY4dVWMGZM8gplgA==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.43.0.tgz", + "integrity": "sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==", "cpu": [ "arm64" ], @@ -1066,9 +1089,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.42.0.tgz", - "integrity": "sha512-rv5UZaWVIJTDMyQ3dCEK+m0SAn6G7H3PRc2AZmExvbDvtaDc+qXkei0knQWcI3+c9tEs7iL/4I4pTQoPbNL2SA==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.43.0.tgz", + "integrity": "sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==", "cpu": [ "x64" ], @@ -1080,9 +1103,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.42.0.tgz", - "integrity": "sha512-fJcN4uSGPWdpVmvLuMtALUFwCHgb2XiQjuECkHT3lWLZhSQ3MBQ9pq+WoWeJq2PrNxr9rPM1Qx+IjyGj8/c6zQ==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.43.0.tgz", + "integrity": "sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==", "cpu": [ "arm64" ], @@ -1094,9 +1117,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.42.0.tgz", - "integrity": "sha512-CziHfyzpp8hJpCVE/ZdTizw58gr+m7Y2Xq5VOuCSrZR++th2xWAz4Nqk52MoIIrV3JHtVBhbBsJcAxs6NammOQ==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.43.0.tgz", + "integrity": "sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==", "cpu": [ "x64" ], @@ -1108,9 +1131,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.42.0.tgz", - "integrity": "sha512-UsQD5fyLWm2Fe5CDM7VPYAo+UC7+2Px4Y+N3AcPh/LdZu23YcuGPegQly++XEVaC8XUTFVPscl5y5Cl1twEI4A==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.43.0.tgz", + "integrity": "sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==", "cpu": [ "arm" ], @@ -1122,9 +1145,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.42.0.tgz", - "integrity": "sha512-/i8NIrlgc/+4n1lnoWl1zgH7Uo0XK5xK3EDqVTf38KvyYgCU/Rm04+o1VvvzJZnVS5/cWSd07owkzcVasgfIkQ==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.43.0.tgz", + "integrity": "sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==", "cpu": [ "arm" ], @@ -1136,9 +1159,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.42.0.tgz", - "integrity": "sha512-eoujJFOvoIBjZEi9hJnXAbWg+Vo1Ov8n/0IKZZcPZ7JhBzxh2A+2NFyeMZIRkY9iwBvSjloKgcvnjTbGKHE44Q==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.43.0.tgz", + "integrity": "sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==", "cpu": [ "arm64" ], @@ -1150,9 +1173,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.42.0.tgz", - "integrity": "sha512-/3NrcOWFSR7RQUQIuZQChLND36aTU9IYE4j+TB40VU78S+RA0IiqHR30oSh6P1S9f9/wVOenHQnacs/Byb824g==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.43.0.tgz", + "integrity": "sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==", "cpu": [ "arm64" ], @@ -1164,9 +1187,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.42.0.tgz", - "integrity": "sha512-O8AplvIeavK5ABmZlKBq9/STdZlnQo7Sle0LLhVA7QT+CiGpNVe197/t8Aph9bhJqbDVGCHpY2i7QyfEDDStDg==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.43.0.tgz", + "integrity": "sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==", "cpu": [ "loong64" ], @@ -1178,9 +1201,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.42.0.tgz", - "integrity": "sha512-6Qb66tbKVN7VyQrekhEzbHRxXXFFD8QKiFAwX5v9Xt6FiJ3BnCVBuyBxa2fkFGqxOCSGGYNejxd8ht+q5SnmtA==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.43.0.tgz", + "integrity": "sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==", "cpu": [ "ppc64" ], @@ -1192,9 +1215,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.42.0.tgz", - "integrity": "sha512-KQETDSEBamQFvg/d8jajtRwLNBlGc3aKpaGiP/LvEbnmVUKlFta1vqJqTrvPtsYsfbE/DLg5CC9zyXRX3fnBiA==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.43.0.tgz", + "integrity": "sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==", "cpu": [ "riscv64" ], @@ -1206,9 +1229,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.42.0.tgz", - "integrity": "sha512-qMvnyjcU37sCo/tuC+JqeDKSuukGAd+pVlRl/oyDbkvPJ3awk6G6ua7tyum02O3lI+fio+eM5wsVd66X0jQtxw==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.43.0.tgz", + "integrity": "sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==", "cpu": [ "riscv64" ], @@ -1220,9 +1243,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.42.0.tgz", - "integrity": "sha512-I2Y1ZUgTgU2RLddUHXTIgyrdOwljjkmcZ/VilvaEumtS3Fkuhbw4p4hgHc39Ypwvo2o7sBFNl2MquNvGCa55Iw==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.43.0.tgz", + "integrity": "sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==", "cpu": [ "s390x" ], @@ -1234,9 +1257,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.42.0.tgz", - "integrity": "sha512-Gfm6cV6mj3hCUY8TqWa63DB8Mx3NADoFwiJrMpoZ1uESbK8FQV3LXkhfry+8bOniq9pqY1OdsjFWNsSbfjPugw==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.43.0.tgz", + "integrity": "sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==", "cpu": [ "x64" ], @@ -1248,9 +1271,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.42.0.tgz", - "integrity": "sha512-g86PF8YZ9GRqkdi0VoGlcDUb4rYtQKyTD1IVtxxN4Hpe7YqLBShA7oHMKU6oKTCi3uxwW4VkIGnOaH/El8de3w==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.43.0.tgz", + "integrity": "sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==", "cpu": [ "x64" ], @@ -1262,9 +1285,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.42.0.tgz", - "integrity": "sha512-+axkdyDGSp6hjyzQ5m1pgcvQScfHnMCcsXkx8pTgy/6qBmWVhtRVlgxjWwDp67wEXXUr0x+vD6tp5W4x6V7u1A==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.43.0.tgz", + "integrity": "sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw==", "cpu": [ "arm64" ], @@ -1276,9 +1299,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.42.0.tgz", - "integrity": "sha512-F+5J9pelstXKwRSDq92J0TEBXn2nfUrQGg+HK1+Tk7VOL09e0gBqUHugZv7SW4MGrYj41oNCUe3IKCDGVlis2g==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.43.0.tgz", + "integrity": "sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw==", "cpu": [ "ia32" ], @@ -1290,9 +1313,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.42.0.tgz", - "integrity": "sha512-LpHiJRwkaVz/LqjHjK8LCi8osq7elmpwujwbXKNW88bM8eeGxavJIKKjkjpMHAh/2xfnrt1ZSnhTv41WYUHYmA==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.43.0.tgz", + "integrity": "sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw==", "cpu": [ "x64" ], @@ -1469,6 +1492,16 @@ "node": ">=8" } }, + "node_modules/@textlint/linter-formatter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/@textlint/linter-formatter/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1476,6 +1509,20 @@ "dev": true, "license": "MIT" }, + "node_modules/@textlint/linter-formatter/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@textlint/linter-formatter/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -1618,6 +1665,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/mdast": { "version": "3.0.15", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", @@ -1629,9 +1683,9 @@ } }, "node_modules/@types/node": { - "version": "22.15.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz", - "integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==", + "version": "22.15.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.31.tgz", + "integrity": "sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==", "dev": true, "license": "MIT", "dependencies": { @@ -1872,14 +1926,11 @@ } }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } + "license": "Python-2.0" }, "node_modules/assertion-error": { "version": "2.0.1", @@ -1928,6 +1979,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, "license": "MIT" }, "node_modules/base64-js": { @@ -1986,9 +2038,10 @@ "license": "BSD-2-Clause" }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -2725,9 +2778,9 @@ } }, "node_modules/fdir": { - "version": "6.4.5", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz", - "integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==", + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", "dev": true, "license": "MIT", "peerDependencies": { @@ -3313,14 +3366,13 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -3354,9 +3406,9 @@ } }, "node_modules/keyv": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.3.3.tgz", - "integrity": "sha512-Rwu4+nXI9fqcxiEHtbkvoes2X+QfkTRo1TMkPfwzipGsJlJO/z69vqB4FNl9xJ3xCpAcbkvmEabZfPzrwN3+gQ==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.3.4.tgz", + "integrity": "sha512-ypEvQvInNpUe+u+w8BIcPkQvEqXquyyibWE/1NB5T2BTzIpS5cGEV1LZskDzPSTvNAaT4+5FutvzlvnkxOSKlw==", "dev": true, "license": "MIT", "dependencies": { @@ -3890,12 +3942,12 @@ } }, "node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { "node": "20 || >=22" @@ -4366,9 +4418,9 @@ "license": "MIT" }, "node_modules/postcss": { - "version": "8.5.4", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz", - "integrity": "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==", + "version": "8.5.5", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.5.tgz", + "integrity": "sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==", "dev": true, "funding": [ { @@ -4483,26 +4535,6 @@ "require-from-string": "^2.0.2" } }, - "node_modules/rc-config-loader/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/rc-config-loader/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -4721,9 +4753,9 @@ } }, "node_modules/rollup": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.42.0.tgz", - "integrity": "sha512-LW+Vse3BJPyGJGAJt1j8pWDKPd73QM8cRXYK1IxOBgL2AGLu7Xd2YOW0M2sLUBCkF5MshXXtMApyEAEzMVMsnw==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.43.0.tgz", + "integrity": "sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==", "dev": true, "license": "MIT", "dependencies": { @@ -4737,26 +4769,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.42.0", - "@rollup/rollup-android-arm64": "4.42.0", - "@rollup/rollup-darwin-arm64": "4.42.0", - "@rollup/rollup-darwin-x64": "4.42.0", - "@rollup/rollup-freebsd-arm64": "4.42.0", - "@rollup/rollup-freebsd-x64": "4.42.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.42.0", - "@rollup/rollup-linux-arm-musleabihf": "4.42.0", - "@rollup/rollup-linux-arm64-gnu": "4.42.0", - "@rollup/rollup-linux-arm64-musl": "4.42.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.42.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.42.0", - "@rollup/rollup-linux-riscv64-gnu": "4.42.0", - "@rollup/rollup-linux-riscv64-musl": "4.42.0", - "@rollup/rollup-linux-s390x-gnu": "4.42.0", - "@rollup/rollup-linux-x64-gnu": "4.42.0", - "@rollup/rollup-linux-x64-musl": "4.42.0", - "@rollup/rollup-win32-arm64-msvc": "4.42.0", - "@rollup/rollup-win32-ia32-msvc": "4.42.0", - "@rollup/rollup-win32-x64-msvc": "4.42.0", + "@rollup/rollup-android-arm-eabi": "4.43.0", + "@rollup/rollup-android-arm64": "4.43.0", + "@rollup/rollup-darwin-arm64": "4.43.0", + "@rollup/rollup-darwin-x64": "4.43.0", + "@rollup/rollup-freebsd-arm64": "4.43.0", + "@rollup/rollup-freebsd-x64": "4.43.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.43.0", + "@rollup/rollup-linux-arm-musleabihf": "4.43.0", + "@rollup/rollup-linux-arm64-gnu": "4.43.0", + "@rollup/rollup-linux-arm64-musl": "4.43.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.43.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.43.0", + "@rollup/rollup-linux-riscv64-gnu": "4.43.0", + "@rollup/rollup-linux-riscv64-musl": "4.43.0", + "@rollup/rollup-linux-s390x-gnu": "4.43.0", + "@rollup/rollup-linux-x64-gnu": "4.43.0", + "@rollup/rollup-linux-x64-musl": "4.43.0", + "@rollup/rollup-win32-arm64-msvc": "4.43.0", + "@rollup/rollup-win32-ia32-msvc": "4.43.0", + "@rollup/rollup-win32-x64-msvc": "4.43.0", "fsevents": "~2.3.2" } }, @@ -5567,9 +5599,9 @@ } }, "node_modules/tsx": { - "version": "4.19.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.4.tgz", - "integrity": "sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q==", + "version": "4.20.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.2.tgz", + "integrity": "sha512-He0ZWr41gLa4vD30Au3yuwpe0HXaCZbclvl8RBieUiJ9aFnPMWUPIyvw3RU8+1Crjfcrauvitae2a4tUzRAGsw==", "dev": true, "license": "MIT", "dependencies": { @@ -6193,9 +6225,9 @@ } }, "node_modules/zod": { - "version": "3.25.57", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.57.tgz", - "integrity": "sha512-6tgzLuwVST5oLUxXTmBqoinKMd3JeesgbgseXeFasKKj8Q1FCZrHnbqJOyiEvr4cVAlbug+CgIsmJ8cl/pU5FA==", + "version": "3.25.63", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.63.tgz", + "integrity": "sha512-3ttCkqhtpncYXfP0f6dsyabbYV/nEUW+Xlu89jiXbTBifUfjaSqXOG6JnQPLtqt87n7KAmnMqcjay6c0Wq0Vbw==", "dev": true, "license": "MIT", "funding": { diff --git a/package.json b/package.json index 3ff85fb4..bf9fb129 100644 --- a/package.json +++ b/package.json @@ -61,10 +61,12 @@ "@biomejs/biome": "^1.9.4", "@octokit/types": "^14.0.0", "@octokit/webhooks-types": "^7.6.1", + "@types/js-yaml": "^4.0.9", "@types/node": "^22.15.29", "@types/which": "^3.0.4", "@vercel/ncc": "^0.38.3", "@vitest/coverage-v8": "^3.1.3", + "js-yaml": "^4.1.0", "make-coverage-badge": "^1.2.0", "openai": "latest", "textlint": "^14.8.0", From 1b72627d7e86dd552a9ae0a9ad4b748224f4a6a6 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Mon, 16 Jun 2025 16:36:01 +0000 Subject: [PATCH 08/29] chore: update package dependencies to latest versions --- package-lock.json | 231 +++++++++++++++++++++++----------------------- 1 file changed, 117 insertions(+), 114 deletions(-) diff --git a/package-lock.json b/package-lock.json index 35231e0e..791e5150 100644 --- a/package-lock.json +++ b/package-lock.json @@ -879,9 +879,9 @@ } }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.12.1.tgz", - "integrity": "sha512-KG1CZhZfWg+u8pxeM/mByJDScJSrjjxLc8fwQqbsS8xCjBmQfMNEBTotYdNanKekepnfRI85GtgQlctLFpcYPw==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.12.3.tgz", + "integrity": "sha512-DyVYSOafBvk3/j1Oka4z5BWT8o4AFmoNyZY9pALOm7Lh3GZglR71Co4r4dEUoqDWdDazIZQHBe7J2Nwkg6gHgQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1327,66 +1327,66 @@ ] }, "node_modules/@textlint/ast-node-types": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-14.8.0.tgz", - "integrity": "sha512-CARGqRSX+DhHdSYssa6+Yb0KAk5cGPDOgKbJo/H8djJAmw7qNzo/oYbuYZlO/fqmUbZjZcvI/6QgCxa/78Nxew==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-14.8.4.tgz", + "integrity": "sha512-+fI7miec/r9VeniFV9ppL4jRCmHNsTxieulTUf/4tvGII3db5hGriKHC4p/diq1SkQ9Sgs7kg6UyydxZtpTz1Q==", "dev": true, "license": "MIT" }, "node_modules/@textlint/ast-tester": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/@textlint/ast-tester/-/ast-tester-14.8.0.tgz", - "integrity": "sha512-nRsgHmY+O7OhCYwGyWze+8mhnTYfCPFYTvuF3mCE5nQrfO9y2anvdjj2Yf6FU7OZI7qxp/R7MWYkiIQb/WEfHQ==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/@textlint/ast-tester/-/ast-tester-14.8.4.tgz", + "integrity": "sha512-j6YKPuEaASeXQ2Y/ode993r4A8ugdGEFnPhp96HVGjNVoAsandlR/L0WEMDG1FdIJj3W9+9rlcikXhFQSFc0lA==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "^14.8.0", + "@textlint/ast-node-types": "14.8.4", "debug": "^4.4.1" } }, "node_modules/@textlint/ast-traverse": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/@textlint/ast-traverse/-/ast-traverse-14.8.0.tgz", - "integrity": "sha512-/u1SiIVnRFm1D/pglLtaP0QJND7UAo8axrUfaikFJZ67ciiguu17/yB0VBMbx9iZn5bmeUHH1NicgCnuirvvJg==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/@textlint/ast-traverse/-/ast-traverse-14.8.4.tgz", + "integrity": "sha512-bnmgt0dB5RxBhRXQnaTd6wblfuv+cRWrGuyMp6CIuPTyWXyA5AO3NhqQYjQLCbrPDByiwbHAQwIZYOw6sVvn9Q==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "^14.8.0" + "@textlint/ast-node-types": "14.8.4" } }, "node_modules/@textlint/config-loader": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/@textlint/config-loader/-/config-loader-14.8.0.tgz", - "integrity": "sha512-WYg2WhyFCcCmEN1HOOpe420CMg9o7HzbELVGWvNrgNqqmQDxusUX88z1IG2xJ1bIGpgZTbm9SneUBnoRTzPCJg==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/@textlint/config-loader/-/config-loader-14.8.4.tgz", + "integrity": "sha512-TWIfYkGIl6zZz4GJWQVrWurK25YG0j0Br/Jexn2EAh7sun5wDsb7hHK1Y2aWHIAeWHOn5D2C0OdHT3jH8YToGA==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/kernel": "^14.8.0", - "@textlint/module-interop": "^14.8.0", - "@textlint/resolver": "^14.8.0", - "@textlint/types": "^14.8.0", - "@textlint/utils": "^14.8.0", + "@textlint/kernel": "14.8.4", + "@textlint/module-interop": "14.8.4", + "@textlint/resolver": "14.8.4", + "@textlint/types": "14.8.4", + "@textlint/utils": "14.8.4", "debug": "^4.4.1", "rc-config-loader": "^4.1.3" } }, "node_modules/@textlint/feature-flag": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/@textlint/feature-flag/-/feature-flag-14.8.0.tgz", - "integrity": "sha512-cmqPYs1EUYC/5YE8pd70ODrtHCeumR5kamK+CuNj2tS2lDPJ55XJt2I+UnX0SqyRATdl7Yp7OizLfZ5xrtAlUQ==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/@textlint/feature-flag/-/feature-flag-14.8.4.tgz", + "integrity": "sha512-bI1HpZtArzgmbPsMubKe3AYLIOYPOqHJ8R8JlhSuduszVd6gFsyptmMTHdI+1gWRTo1Dv9LRGEmI9W9rAV7Dmg==", "dev": true, "license": "MIT" }, "node_modules/@textlint/fixer-formatter": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/@textlint/fixer-formatter/-/fixer-formatter-14.8.0.tgz", - "integrity": "sha512-eDpH/GQrod3Jg4HNkXw4SclSWLX85snUhzhMo1wmYgI8inRaIzfd1sURRy6edEabd6y1kLImyFmYOx9U96ILQA==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/@textlint/fixer-formatter/-/fixer-formatter-14.8.4.tgz", + "integrity": "sha512-lpEaVF1iUBL4d+X04BIus7ubiPk5PeRmriFosxoCKT9RqJFXMnC6ApBGpWX5fLBTRK9XNesOpP0c+tXprOAPdw==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/module-interop": "^14.8.0", - "@textlint/resolver": "^14.8.0", - "@textlint/types": "^14.8.0", + "@textlint/module-interop": "14.8.4", + "@textlint/resolver": "14.8.4", + "@textlint/types": "14.8.4", "chalk": "^4.1.2", "debug": "^4.4.1", "diff": "^5.2.0", @@ -1441,36 +1441,36 @@ } }, "node_modules/@textlint/kernel": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/@textlint/kernel/-/kernel-14.8.0.tgz", - "integrity": "sha512-AUy2qK7Z1pWBwtHjb3kdCwKjPo6M5SuS+e/Homvn77oUKXJVtJi4+HpDvVxTRZjW37LtYxIr/CyNhhNN8HAu4Q==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/@textlint/kernel/-/kernel-14.8.4.tgz", + "integrity": "sha512-fBk8Lm4Ph7ogvqpSpRFiB0NM/rQVWOnOMLSJqZsdyvA40IVeZZYs+2bM1WgVdAZLUQTHSzKMExsHu2c91YVpKw==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "^14.8.0", - "@textlint/ast-tester": "^14.8.0", - "@textlint/ast-traverse": "^14.8.0", - "@textlint/feature-flag": "^14.8.0", - "@textlint/source-code-fixer": "^14.8.0", - "@textlint/types": "^14.8.0", - "@textlint/utils": "^14.8.0", + "@textlint/ast-node-types": "14.8.4", + "@textlint/ast-tester": "14.8.4", + "@textlint/ast-traverse": "14.8.4", + "@textlint/feature-flag": "14.8.4", + "@textlint/source-code-fixer": "14.8.4", + "@textlint/types": "14.8.4", + "@textlint/utils": "14.8.4", "debug": "^4.4.1", "fast-equals": "^4.0.3", "structured-source": "^4.0.0" } }, "node_modules/@textlint/linter-formatter": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-14.8.0.tgz", - "integrity": "sha512-xedTBR/rlVNS9Np5moxhWGwVWEY8Eg+MxkasZblEZgiGbuMhCiVNrSuvx8T6E/UFvFunQYc9oNIMylzAXtnx7A==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-14.8.4.tgz", + "integrity": "sha512-sZ0UfYRDBNHnfMVBqLqqYnqTB7Ec169ljlmo+SEHR1T+dHUPYy1/DZK4p7QREXlBSFL4cnkswETCbc9xRodm4Q==", "dev": true, "license": "MIT", "dependencies": { "@azu/format-text": "^1.0.2", "@azu/style-format": "^1.0.1", - "@textlint/module-interop": "^14.8.0", - "@textlint/resolver": "^14.8.0", - "@textlint/types": "^14.8.0", + "@textlint/module-interop": "14.8.4", + "@textlint/resolver": "14.8.4", + "@textlint/types": "14.8.4", "chalk": "^4.1.2", "debug": "^4.4.1", "js-yaml": "^3.14.1", @@ -1552,92 +1552,95 @@ } }, "node_modules/@textlint/markdown-to-ast": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/@textlint/markdown-to-ast/-/markdown-to-ast-14.8.0.tgz", - "integrity": "sha512-KxvogGH8BPfR4eP5TNlRiR7KcPWzFjk1k8TX0WBnqWTzQeYbDYulYslVyiq06qc1NkTHvpK34zbS8UWZyzIQKA==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/@textlint/markdown-to-ast/-/markdown-to-ast-14.8.4.tgz", + "integrity": "sha512-9x7xqpk//79nREP4Hb219UG3N3lERNorlhXOl1XX4A0y8BcDAKKDv70WftkF9VZ+sx4ys4dv/iOsBA29I0nNQA==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "^14.8.0", + "@textlint/ast-node-types": "14.8.4", "debug": "^4.4.1", "mdast-util-gfm-autolink-literal": "^0.1.3", - "neotraverse": "^0.6.15", + "neotraverse": "^0.6.18", "remark-footnotes": "^3.0.0", "remark-frontmatter": "^3.0.0", "remark-gfm": "^1.0.0", "remark-parse": "^9.0.0", + "structured-source": "^4.0.0", "unified": "^9.2.2" } }, "node_modules/@textlint/module-interop": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-14.8.0.tgz", - "integrity": "sha512-SGeojZIpjP58RrnUAIjKO5xokloHfXJWcc3dh/QP9pDHRCI97yPJhyEXzOD3FiY9zFG2KNIYwUTyrIGnnBm9xQ==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-14.8.4.tgz", + "integrity": "sha512-1LdPYLAVpa27NOt6EqvuFO99s4XLB0c19Hw9xKSG6xQ1K82nUEyuWhzTQKb3KJ5Qx7qj14JlXZLfnEuL6A16Bw==", "dev": true, "license": "MIT" }, "node_modules/@textlint/resolver": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-14.8.0.tgz", - "integrity": "sha512-FUrXlwbfLxSUvgjOG/OgDV56m0IBBswcOEoex8cXAE1677ejAAWrI9WqzuBItX5g+srh5is3Vth4D6H8iuyLAg==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-14.8.4.tgz", + "integrity": "sha512-nMDOgDAVwNU9ommh+Db0U+MCMNDPbQ/1HBNjbnHwxZkCpcT6hsAJwBe38CW/DtWVUv8yeR4R40IYNPT84srNwA==", "dev": true, "license": "MIT" }, "node_modules/@textlint/source-code-fixer": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/@textlint/source-code-fixer/-/source-code-fixer-14.8.0.tgz", - "integrity": "sha512-/BtG3IRpmUG3A0Pr2wra2uY0d4sjmESeJlYn++ZlP8eYNYM9jotWsdb9K+fa1jMX4OzozKv1nOewhSfo/AD6Cw==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/@textlint/source-code-fixer/-/source-code-fixer-14.8.4.tgz", + "integrity": "sha512-/BTSLTgpRqrgwqB2Jmu/sRMEgB3sn9dxhDRmSX4hFFbtD2wT8/d4TcxD7rTe3NdWAPCCHQ8xCBUHDuZrTqDA4w==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/types": "^14.8.0", + "@textlint/types": "14.8.4", "debug": "^4.4.1" } }, "node_modules/@textlint/text-to-ast": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/@textlint/text-to-ast/-/text-to-ast-14.8.0.tgz", - "integrity": "sha512-/U8k2Y6azqeKJnEJej2b8dylqKZw4tSqsOlfeI82qslDEjjdtseuzbLz7Z0X/VgWmbnqxrt1y/ZsLaThhOntuQ==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/@textlint/text-to-ast/-/text-to-ast-14.8.4.tgz", + "integrity": "sha512-BWWEM12WqWUKmI9BQvnjtu4CElExWhm1asPE3j//jFTyR6oLv14NaFUaR26xGJWAI28WIa293AmWfE60ygHdRA==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "^14.8.0" + "@textlint/ast-node-types": "14.8.4" } }, "node_modules/@textlint/textlint-plugin-markdown": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-markdown/-/textlint-plugin-markdown-14.8.0.tgz", - "integrity": "sha512-BDjcoBfv+Vxg83/GrTg9XK4wKIuZb7x85gLmRqlsy48Lj5l++AIk5qe/iL1hI38PDr8IfY8JRYvfMpyn+KGuNA==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-markdown/-/textlint-plugin-markdown-14.8.4.tgz", + "integrity": "sha512-WWFo05mIsXaJPrWiR/nsvaLd/nUS0xWWeJg6AcpOkrxyIqH//PyTuQHD9sYpJkCFopWP1/8GeCba+a/m2llX4g==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/markdown-to-ast": "^14.8.0" + "@textlint/markdown-to-ast": "14.8.4", + "@textlint/types": "14.8.4" } }, "node_modules/@textlint/textlint-plugin-text": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-text/-/textlint-plugin-text-14.8.0.tgz", - "integrity": "sha512-fg2382TsRL7FiWxatvr3VNyjIQqMTRIqFkuPjBSb8HjC5xabi5s2ZU8Z0O58SBN9YWpihcTP+N84W5l5iU784g==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-text/-/textlint-plugin-text-14.8.4.tgz", + "integrity": "sha512-FY7H9a2I07/DzQtouQK9/Fs+9fgMAw5xQvHgAiqOffGU/i8WvWnsywflciW/IRi/By1TCd5nhdN/YRBvzuvfnw==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/text-to-ast": "^14.8.0" + "@textlint/text-to-ast": "14.8.4", + "@textlint/types": "14.8.4" } }, "node_modules/@textlint/types": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/@textlint/types/-/types-14.8.0.tgz", - "integrity": "sha512-Lsq2gxh2pmCKV6KN4fL70DMNNGuZlPuDQ0RHLU59/wgUs5krzrpHWCRYHK4M4J45U1PfZzQnWvLIfFETlR9GPA==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-14.8.4.tgz", + "integrity": "sha512-9nyY8vVXlr8hHKxa6+37omJhXWCwovMQcgMteuldYd4dOxGm14AK2nXdkgtKEUQnzLGaXy46xwLCfhQy7V7/YA==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "^14.8.0" + "@textlint/ast-node-types": "14.8.4" } }, "node_modules/@textlint/utils": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/@textlint/utils/-/utils-14.8.0.tgz", - "integrity": "sha512-ZYZuyPl7EW1Tio/jfjf92MFgPwrQ6nklir5uCJAwrdl9Me/9rL7l3n8HlFHc8Z7dPNeqUbDuOLgoS+74coZplA==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/@textlint/utils/-/utils-14.8.4.tgz", + "integrity": "sha512-ByRbUBtxhvZoI43CJJCy0oVPwpvB4/r8FhH33QguW9DSVk33y8ful5YIhV8ziSGjNJbwxGhe3rqR8YBmUkrnsQ==", "dev": true, "license": "MIT" }, @@ -1683,9 +1686,9 @@ } }, "node_modules/@types/node": { - "version": "22.15.31", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.31.tgz", - "integrity": "sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==", + "version": "22.15.32", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.32.tgz", + "integrity": "sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA==", "dev": true, "license": "MIT", "dependencies": { @@ -3486,9 +3489,9 @@ } }, "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", + "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", "dev": true, "license": "MIT" }, @@ -4418,9 +4421,9 @@ "license": "MIT" }, "node_modules/postcss": { - "version": "8.5.5", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.5.tgz", - "integrity": "sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, "funding": [ { @@ -5421,32 +5424,32 @@ "license": "MIT" }, "node_modules/textlint": { - "version": "14.8.0", - "resolved": "https://registry.npmjs.org/textlint/-/textlint-14.8.0.tgz", - "integrity": "sha512-1+Y78J7b509CagmxxhceRRF99KXNuUBjstMIGc2pp/CkjY/vdr6s1AObWMiiWF7asm3UFmgfqMxl+tNIlvuT5Q==", + "version": "14.8.4", + "resolved": "https://registry.npmjs.org/textlint/-/textlint-14.8.4.tgz", + "integrity": "sha512-oV7DwKjdbIk+5LlAhtTtWsudzNdUnEpP2KW2iIRnjdZ0uM/vXhffDh66UL6P3nk7Io37qhSRb3E82fdVHqyblw==", "dev": true, "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.12.1", - "@textlint/ast-node-types": "^14.8.0", - "@textlint/ast-traverse": "^14.8.0", - "@textlint/config-loader": "^14.8.0", - "@textlint/feature-flag": "^14.8.0", - "@textlint/fixer-formatter": "^14.8.0", - "@textlint/kernel": "^14.8.0", - "@textlint/linter-formatter": "^14.8.0", - "@textlint/module-interop": "^14.8.0", - "@textlint/resolver": "^14.8.0", - "@textlint/textlint-plugin-markdown": "^14.8.0", - "@textlint/textlint-plugin-text": "^14.8.0", - "@textlint/types": "^14.8.0", - "@textlint/utils": "^14.8.0", + "@textlint/ast-node-types": "14.8.4", + "@textlint/ast-traverse": "14.8.4", + "@textlint/config-loader": "14.8.4", + "@textlint/feature-flag": "14.8.4", + "@textlint/fixer-formatter": "14.8.4", + "@textlint/kernel": "14.8.4", + "@textlint/linter-formatter": "14.8.4", + "@textlint/module-interop": "14.8.4", + "@textlint/resolver": "14.8.4", + "@textlint/textlint-plugin-markdown": "14.8.4", + "@textlint/textlint-plugin-text": "14.8.4", + "@textlint/types": "14.8.4", + "@textlint/utils": "14.8.4", "debug": "^4.4.1", - "file-entry-cache": "^10.0.5", + "file-entry-cache": "^10.0.8", "glob": "^10.4.5", "md5": "^2.3.0", "mkdirp": "^0.5.6", - "optionator": "^0.9.3", + "optionator": "^0.9.4", "path-to-glob-pattern": "^2.0.1", "rc-config-loader": "^4.1.3", "read-pkg": "^1.1.0", @@ -5599,9 +5602,9 @@ } }, "node_modules/tsx": { - "version": "4.20.2", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.2.tgz", - "integrity": "sha512-He0ZWr41gLa4vD30Au3yuwpe0HXaCZbclvl8RBieUiJ9aFnPMWUPIyvw3RU8+1Crjfcrauvitae2a4tUzRAGsw==", + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", + "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6225,9 +6228,9 @@ } }, "node_modules/zod": { - "version": "3.25.63", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.63.tgz", - "integrity": "sha512-3ttCkqhtpncYXfP0f6dsyabbYV/nEUW+Xlu89jiXbTBifUfjaSqXOG6JnQPLtqt87n7KAmnMqcjay6c0Wq0Vbw==", + "version": "3.25.64", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.64.tgz", + "integrity": "sha512-hbP9FpSZf7pkS7hRVUrOjhwKJNyampPgtXKc3AN6DsWtoHsg2Sb4SQaS4Tcay380zSwd2VPo9G9180emBACp5g==", "dev": true, "license": "MIT", "funding": { From 341fecce2fbb991713fe18a883fa1ed6ec426cfe Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Mon, 16 Jun 2025 16:43:49 +0000 Subject: [PATCH 09/29] fix: remove unnecessary SSH agent forwarding and update prettier feature --- .devcontainer/devcontainer.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 686509c8..9bf68b61 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -26,7 +26,6 @@ "markdown.extension.list.indentationSize": "adaptive", "markdown.extension.italic.indicator": "_", "markdown.extension.orderedList.marker": "one", - "remote.SSH.enableAgentForwarding": true, "[json]": { "editor.defaultFormatter": "biomejs.biome" }, @@ -52,7 +51,6 @@ "GITHUB_TOKEN": "${localEnv:GITHUB_TOKEN}" }, "features": { - "ghcr.io/devcontainers/features/github-cli:1": {}, - "ghcr.io/devcontainers-contrib/features/prettier:1": {} + "ghcr.io/devcontainers-community/npm-features/prettier": {} } } From 3b26ad49bb960156b890c5a51d11b4afd5d8ca97 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Mon, 16 Jun 2025 16:46:07 +0000 Subject: [PATCH 10/29] fix: update super-linter action to specific version for consistency Fixes: https://app.aikido.dev/repositories/462873?sidebarIssue=3135661 --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 19e2f655..4cf426c9 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -42,7 +42,7 @@ jobs: - name: Lint Codebase id: super-linter - uses: super-linter/super-linter/slim@v7 + uses: super-linter/super-linter@12150456a73e248bdc94d0794898f94e23127c88 # v7.4.0 env: DEFAULT_BRANCH: main GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 2a490b2853c5783c17572659f9bb96a00ffd0415 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Mon, 16 Jun 2025 16:48:54 +0000 Subject: [PATCH 11/29] fix: update create-pull-request action to specific version for stability https://app.aikido.dev/repositories/462873?sidebarIssue=3135661 --- .github/workflows/release-start.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-start.yml b/.github/workflows/release-start.yml index abfb5db7..1c498dc9 100644 --- a/.github/workflows/release-start.yml +++ b/.github/workflows/release-start.yml @@ -149,7 +149,7 @@ jobs: } - name: Create Branch and Pull Request - uses: peter-evans/create-pull-request@v7 + uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 with: token: ${{ steps.app-token.outputs.token }} base: main From b80ea3fd8a0a49485741529f38eb2d30c945e321 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Mon, 16 Jun 2025 16:53:00 +0000 Subject: [PATCH 12/29] fix: update SonarQube scan action to specific commit SHA for stability --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7b87f0e0..f3d077b9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,6 +36,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GH_TOKEN_REPO_CI_TESTING }} - name: SonarQube Scan - uses: SonarSource/sonarqube-scan-action@v5 + uses: SonarSource/sonarqube-scan-action@2500896589ef8f7247069a56136f8dc177c27ccf # v5 env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From 0c654e221884e5fc6be2dd2cacdb81f4f326a6f2 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Thu, 19 Jun 2025 03:55:26 +0000 Subject: [PATCH 13/29] fix: update package dependencies to latest versions for improved stability --- package-lock.json | 130 +++++++++++++++++++++++----------------------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/package-lock.json b/package-lock.json index 791e5150..3bfc3c8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -879,9 +879,9 @@ } }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.12.3.tgz", - "integrity": "sha512-DyVYSOafBvk3/j1Oka4z5BWT8o4AFmoNyZY9pALOm7Lh3GZglR71Co4r4dEUoqDWdDazIZQHBe7J2Nwkg6gHgQ==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.13.0.tgz", + "integrity": "sha512-P5FZsXU0kY881F6Hbk9GhsYx02/KgWK1DYf7/tyE/1lcFKhDYPQR9iYjhQXJn+Sg6hQleMo3DB7h7+p4wgp2Lw==", "dev": true, "license": "MIT", "dependencies": { @@ -962,9 +962,9 @@ "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-13.0.1.tgz", - "integrity": "sha512-m1KvHlueScy4mQJWvFDCxFBTIdXS0K1SgFGLmqHyX90mZdCIv6gWBbKRhatxRjhGlONuTK/hztYdaqrTXcFZdQ==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-13.1.0.tgz", + "integrity": "sha512-16iNOa4rTTjaWtfsPGJcYYL79FJakseX8TQFIPfVuSPC3s5nkS/DSNQPFPc5lJHgEDBWNMxSApHrEymNblhA9w==", "license": "MIT", "dependencies": { "@octokit/types": "^14.1.0" @@ -1720,9 +1720,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.3.tgz", - "integrity": "sha512-D1QKzngg8PcDoCE8FHSZhREDuEy+zcKmMiMafYse41RZpBE5EDJyKOTdqK3RQfsV2S2nyKor5KCs8PyPRFqKPg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", + "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1744,8 +1744,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.2.3", - "vitest": "3.2.3" + "@vitest/browser": "3.2.4", + "vitest": "3.2.4" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -1754,15 +1754,15 @@ } }, "node_modules/@vitest/expect": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.3.tgz", - "integrity": "sha512-W2RH2TPWVHA1o7UmaFKISPvdicFJH+mjykctJFoAkUw+SPTJTGjUNdKscFBrqM7IPnCVu6zihtKYa7TkZS1dkQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", "dev": true, "license": "MIT", "dependencies": { "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.3", - "@vitest/utils": "3.2.3", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -1771,13 +1771,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.3.tgz", - "integrity": "sha512-cP6fIun+Zx8he4rbWvi+Oya6goKQDZK+Yq4hhlggwQBbrlOQ4qtZ+G4nxB6ZnzI9lyIb+JnvyiJnPC2AGbKSPA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.2.3", + "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -1798,9 +1798,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.3.tgz", - "integrity": "sha512-yFglXGkr9hW/yEXngO+IKMhP0jxyFw2/qys/CK4fFUZnSltD+MU7dVYGrH8rvPcK/O6feXQA+EU33gjaBBbAng==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", "dev": true, "license": "MIT", "dependencies": { @@ -1811,13 +1811,13 @@ } }, "node_modules/@vitest/runner": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.3.tgz", - "integrity": "sha512-83HWYisT3IpMaU9LN+VN+/nLHVBCSIUKJzGxC5RWUOsK1h3USg7ojL+UXQR3b4o4UBIWCYdD2fxuzM7PQQ1u8w==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.2.3", + "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" }, @@ -1826,13 +1826,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.3.tgz", - "integrity": "sha512-9gIVWx2+tysDqUmmM1L0hwadyumqssOL1r8KJipwLx5JVYyxvVRfxvMq7DaWbZZsCqZnu/dZedaZQh4iYTtneA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.3", + "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -1841,9 +1841,9 @@ } }, "node_modules/@vitest/spy": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.3.tgz", - "integrity": "sha512-JHu9Wl+7bf6FEejTCREy+DmgWe+rQKbK+y32C/k5f4TBIAlijhJbRBIRIOCEpVevgRsCQR2iHRUH2/qKVM/plw==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", "dev": true, "license": "MIT", "dependencies": { @@ -1854,14 +1854,14 @@ } }, "node_modules/@vitest/utils": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.3.tgz", - "integrity": "sha512-4zFBCU5Pf+4Z6v+rwnZ1HU1yzOKKvDkMXZrymE2PBlbjKJRlrOxbvpfPSvJTGRIwGoahaOGvp+kbCoxifhzJ1Q==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.3", - "loupe": "^3.1.3", + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" }, "funding": { @@ -4118,9 +4118,9 @@ } }, "node_modules/openai": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-5.3.0.tgz", - "integrity": "sha512-VIKmoF7y4oJCDOwP/oHXGzM69+x0dpGFmN9QmYO+uPbLFOmmnwO+x1GbsgUtI+6oraxomGZ566Y421oYVu191w==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/openai/-/openai-5.5.1.tgz", + "integrity": "sha512-5i19097mGotHA1eFsM6Tjd/tJ8uo9sa5Ysv4Q6bKJ2vtN6rc0MzMrUefXnLXYAJcmMQrC1Efhj0AvfIkXrQamw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5541,9 +5541,9 @@ } }, "node_modules/tinypool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.0.tgz", - "integrity": "sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", "dev": true, "license": "MIT", "engines": { @@ -5976,9 +5976,9 @@ } }, "node_modules/vite-node": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.3.tgz", - "integrity": "sha512-gc8aAifGuDIpZHrPjuHyP4dpQmYXqWw7D1GmDnWeNWP654UEXzVfQ5IHPSK5HaHkwB/+p1atpYpSdw/2kOv8iQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", "dev": true, "license": "MIT", "dependencies": { @@ -5999,20 +5999,20 @@ } }, "node_modules/vitest": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.3.tgz", - "integrity": "sha512-E6U2ZFXe3N/t4f5BwUaVCKRLHqUpk1CBWeMh78UT4VaTPH/2dyvH6ALl29JTovEPu9dVKr/K/J4PkXgrMbw4Ww==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", "dependencies": { "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.3", - "@vitest/mocker": "3.2.3", - "@vitest/pretty-format": "^3.2.3", - "@vitest/runner": "3.2.3", - "@vitest/snapshot": "3.2.3", - "@vitest/spy": "3.2.3", - "@vitest/utils": "3.2.3", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", @@ -6023,10 +6023,10 @@ "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", - "tinypool": "^1.1.0", + "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.3", + "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "bin": { @@ -6042,8 +6042,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.3", - "@vitest/ui": "3.2.3", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, @@ -6228,9 +6228,9 @@ } }, "node_modules/zod": { - "version": "3.25.64", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.64.tgz", - "integrity": "sha512-hbP9FpSZf7pkS7hRVUrOjhwKJNyampPgtXKc3AN6DsWtoHsg2Sb4SQaS4Tcay380zSwd2VPo9G9180emBACp5g==", + "version": "3.25.67", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", + "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", "dev": true, "license": "MIT", "funding": { From b32f5e0868268d51283e4d60bc761c062a016710 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Thu, 19 Jun 2025 06:08:50 +0000 Subject: [PATCH 14/29] feat: support optional v prefix with directory separator config - Removed deprecated action-metadata types and utility functions. - Introduced new metadata structure for action inputs in `metadata.ts`. - Updated configuration handling to support dynamic input mapping. - Enhanced tag processing to accommodate various directory separators. - Added tests for metadata input handling and error scenarios. - Improved regex patterns for module tag validation. - Updated action.yml to include new input parameters for tag directory separator and version prefix usage. - Refactored TerraformModule class to utilize new configuration settings for versioning. --- __tests__/config.test.ts | 25 +- __tests__/context.test.ts | 4 +- __tests__/helpers/action-defaults.ts | 2 +- __tests__/helpers/inputs.ts | 2 +- __tests__/terraform-module.test.ts | 248 +++++++++++++++++- __tests__/utils/metadata.test.ts | 24 ++ action.yml | 8 + src/config.ts | 11 +- src/terraform-module.ts | 131 +++++---- src/types/config.types.ts | 3 +- src/types/index.ts | 2 +- ...on-metadata.types.ts => metadata.types.ts} | 0 src/utils/constants.ts | 35 ++- src/utils/{action-metadata.ts => metadata.ts} | 0 14 files changed, 415 insertions(+), 80 deletions(-) create mode 100644 __tests__/utils/metadata.test.ts rename src/types/{action-metadata.types.ts => metadata.types.ts} (100%) rename src/utils/{action-metadata.ts => metadata.ts} (100%) diff --git a/__tests__/config.test.ts b/__tests__/config.test.ts index a3647f11..1cc95cf7 100644 --- a/__tests__/config.test.ts +++ b/__tests__/config.test.ts @@ -9,6 +9,7 @@ import { setupTestInputs, stringInputs, } from '@/tests/helpers/inputs'; +import { VALID_TAG_DIRECTORY_SEPARATORS } from '@/utils/constants'; import { endGroup, getBooleanInput, getInput, info, startGroup } from '@actions/core'; import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; @@ -191,22 +192,32 @@ describe('config', () => { it('should throw error for invalid tag directory separator length', () => { setupTestInputs({ 'tag-directory-separator': 'ab' }); - expect(() => getConfig()).toThrow( - new TypeError('Tag directory separator must be exactly one character'), - ); + expect(() => getConfig()).toThrow(new TypeError('Tag directory separator must be exactly one character')); }); it('should throw error for invalid tag directory separator character', () => { setupTestInputs({ 'tag-directory-separator': '@' }); expect(() => getConfig()).toThrow( - new TypeError("Tag directory separator must be one of: -, _, /, .. Got: '@'"), + new TypeError(`Tag directory separator must be one of: ${VALID_TAG_DIRECTORY_SEPARATORS.join(', ')}. Got: '@'`), ); }); + it('should allow valid tag directory separators', () => { + for (const separator of VALID_TAG_DIRECTORY_SEPARATORS) { + clearConfigForTesting(); + vi.unstubAllEnvs(); + setupTestInputs({ 'tag-directory-separator': separator }); + const config = getConfig(); + expect(config.tagDirectorySeparator).toBe(separator); + } + }); + it('should throw error for invalid default first tag format', () => { setupTestInputs({ 'default-first-tag': 'invalid-tag' }); expect(() => getConfig()).toThrow( - new TypeError("Default first tag must be in format v#.#.# or #.#.# (e.g., v1.0.0 or 1.0.0). Got: 'invalid-tag'"), + new TypeError( + "Default first tag must be in format v#.#.# or #.#.# (e.g., v1.0.0 or 1.0.0). Got: 'invalid-tag'", + ), ); clearConfigForTesting(); @@ -229,7 +240,7 @@ describe('config', () => { it('should initialize with valid default inputs', () => { const config = getConfig(); - expect(config.majorKeywords).toEqual(['major change', 'breaking change', '!']); + expect(config.majorKeywords).toEqual(['major change', 'breaking change']); expect(config.minorKeywords).toEqual(['feat', 'feature']); expect(config.patchKeywords).toEqual(['fix', 'chore', 'docs']); expect(config.defaultFirstTag).toBe('v1.0.0'); @@ -250,7 +261,7 @@ describe('config', () => { expect(startGroup).toHaveBeenCalledTimes(1); expect(endGroup).toHaveBeenCalledTimes(1); expect(vi.mocked(info).mock.calls).toEqual([ - ['Major Keywords: major change, breaking change, !'], + ['Major Keywords: major change, breaking change'], ['Minor Keywords: feat, feature'], ['Patch Keywords: fix, chore, docs'], ['Default First Tag: v1.0.0'], diff --git a/__tests__/context.test.ts b/__tests__/context.test.ts index 17f2f7cb..b84b20cf 100644 --- a/__tests__/context.test.ts +++ b/__tests__/context.test.ts @@ -48,7 +48,7 @@ describe('context', () => { it(`should throw an error if ${envVar} is not set`, () => { // Set the specific environment variable to undefined, but keep others set vi.stubEnv(envVar, undefined); - + expect(() => getContext()).toThrow( new Error( `The ${envVar} environment variable is missing or invalid. This variable should be automatically set by GitHub for each workflow run. If this variable is missing or not correctly set, it indicates a serious issue with the GitHub Actions environment, potentially affecting the execution of subsequent steps in the workflow. Please review the workflow setup or consult the documentation for proper configuration.`, @@ -60,7 +60,6 @@ describe('context', () => { describe('event validation', () => { it('should throw error when event is not pull_request', () => { - vi.stubEnv('GITHUB_EVENT_NAME', 'push'); expect(() => getContext()).toThrow('This workflow is not running in the context of a pull request'); }); @@ -196,7 +195,6 @@ describe('context', () => { // Ensure GITHUB_API_URL is not set to test the default fallback vi.stubEnv('GITHUB_API_URL', undefined); - const context = getContext(); // Check that the context was created with default API URL diff --git a/__tests__/helpers/action-defaults.ts b/__tests__/helpers/action-defaults.ts index f647dc65..ff549e93 100644 --- a/__tests__/helpers/action-defaults.ts +++ b/__tests__/helpers/action-defaults.ts @@ -1,6 +1,6 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; -import { ACTION_INPUTS } from '@/utils/action-metadata'; +import { ACTION_INPUTS } from '@/utils/metadata'; import * as yaml from 'js-yaml'; /** diff --git a/__tests__/helpers/inputs.ts b/__tests__/helpers/inputs.ts index 2a0526f8..0801dbdd 100644 --- a/__tests__/helpers/inputs.ts +++ b/__tests__/helpers/inputs.ts @@ -1,6 +1,6 @@ import { getActionDefaults } from '@/tests/helpers/action-defaults'; import type { Config } from '@/types'; -import { ACTION_INPUTS } from '@/utils/action-metadata'; +import { ACTION_INPUTS } from '@/utils/metadata'; import { vi } from 'vitest'; // Load action defaults once globally diff --git a/__tests__/terraform-module.test.ts b/__tests__/terraform-module.test.ts index 13818d25..e99cdcf5 100644 --- a/__tests__/terraform-module.test.ts +++ b/__tests__/terraform-module.test.ts @@ -33,6 +33,7 @@ describe('TerraformModule', () => { defaultFirstTag: 'v0.1.0', moduleChangeExcludePatterns: [], modulePathIgnore: [], + useVersionPrefix: true, }); }); @@ -245,6 +246,38 @@ describe('TerraformModule', () => { expect(module.getLatestTagVersion()).toBeNull(); }); + it('should handle tags with different separators in getLatestTagVersion', () => { + // Test with different separators to ensure regex works correctly + module.setTags(['tf-modules/test-module/v1.0.0']); + expect(module.getLatestTagVersion()).toBe('v1.0.0'); + + // Test with hyphen separator + module.setTags(['tf-modules-test-module-v2.0.0']); + expect(module.getLatestTagVersion()).toBe('v2.0.0'); + + // Test with underscore separator + module.setTags(['tf-modules_test_module_v3.0.0']); + expect(module.getLatestTagVersion()).toBe('v3.0.0'); + + // Test with dot separator + module.setTags(['tf-modules.test.module.v4.0.0']); + expect(module.getLatestTagVersion()).toBe('v4.0.0'); + + // Test without v prefix + module.setTags(['tf-modules/test-module/5.0.0']); + expect(module.getLatestTagVersion()).toBe('5.0.0'); + }); + + it('should return null when latest tag does not match MODULE_TAG_REGEX', () => { + // Mock getLatestTag to return an invalid format that won't match the regex + vi.spyOn(module, 'getLatestTag').mockReturnValue('invalid-tag-format'); + + expect(module.getLatestTagVersion()).toBeNull(); + + // Restore the original method + vi.restoreAllMocks(); + }); + it('should handle complex version sorting', () => { const tags = [ 'tf-modules/test-module/v1.2.10', @@ -275,14 +308,14 @@ describe('TerraformModule', () => { it('should throw error for tag with no slash (invalid format)', () => { const tags = ['v1.2.3']; expect(() => module.setTags(tags)).toThrow( - "Invalid tag format: 'v1.2.3'. Expected format: 'tf-modules/test-module/v#.#.#' or 'tf-modules/test-module/#.#.#' for module.", + "Invalid tag format: 'v1.2.3'. Expected format: 'tf-modules/test-module[separator]v#.#.#' or 'tf-modules/test-module[separator]#.#.#'.", ); }); it('should throw error for tag with incorrect module name', () => { const tags = ['foo/bar/v9.8.7']; expect(() => module.setTags(tags)).toThrow( - "Invalid tag format: 'foo/bar/v9.8.7'. Expected format: 'tf-modules/test-module/v#.#.#' or 'tf-modules/test-module/#.#.#' for module.", + "Invalid tag format: 'foo/bar/v9.8.7'. Expected format: 'tf-modules/test-module[separator]v#.#.#' or 'tf-modules/test-module[separator]#.#.#'.", ); }); @@ -304,7 +337,6 @@ describe('TerraformModule', () => { expect(module.tags[1]).toBe('tf-modules/test-module/1.2.3'); }); - // Add tests for extractVersionFromTag describe('extractVersionFromTag()', () => { let module: TerraformModule; @@ -473,7 +505,7 @@ describe('TerraformModule', () => { ]; expect(() => module.setReleases(releases)).toThrow( - "Invalid tag format: 'tf-modules/test-module/v1.0'. Expected format: 'tf-modules/test-module/v#.#.#' or 'tf-modules/test-module/#.#.#' for module.", + "Invalid tag format: 'tf-modules/test-module/v1.0'. Expected format: 'tf-modules/test-module[separator]v#.#.#' or 'tf-modules/test-module[separator]#.#.#'.", ); }); @@ -494,7 +526,7 @@ describe('TerraformModule', () => { ]; expect(() => module.setReleases(releases)).toThrow( - "Invalid tag format: 'tf-modules/test-module/vbeta.1.0'. Expected format: 'tf-modules/test-module/v#.#.#' or 'tf-modules/test-module/#.#.#' for module.", + "Invalid tag format: 'tf-modules/test-module/vbeta.1.0'. Expected format: 'tf-modules/test-module[separator]v#.#.#' or 'tf-modules/test-module[separator]#.#.#'.", ); }); @@ -788,6 +820,38 @@ describe('TerraformModule', () => { "Invalid version format: 'invalid-format'. Expected v#.#.# or #.#.# format.", ); }); + + it('should respect useVersionPrefix setting when true (with v prefix)', () => { + // Set useVersionPrefix to true + config.set({ + useVersionPrefix: true, + }); + + module.setTags(['tf-modules/test-module/v1.2.3']); + module.addCommit({ + sha: 'abc123', + message: 'fix: bug fix', + files: ['main.tf'], + }); + + expect(module.getReleaseTagVersion()).toBe('v1.2.4'); + }); + + it('should respect useVersionPrefix setting when false (without v prefix)', () => { + // Set useVersionPrefix to false + config.set({ + useVersionPrefix: false, + }); + + module.setTags(['tf-modules/test-module/v1.2.3']); + module.addCommit({ + sha: 'abc123', + message: 'fix: bug fix', + files: ['main.tf'], + }); + + expect(module.getReleaseTagVersion()).toBe('1.2.4'); // No 'v' prefix + }); }); describe('getReleaseTag()', () => { @@ -954,17 +1018,48 @@ describe('TerraformModule', () => { TerraformModule.isModuleAssociatedWithTag('tf-modules/vpc-endpoint', 'tf-modules/vpc-endpoint/v1.0.0'), ).toBe(true); expect( - TerraformModule.isModuleAssociatedWithTag('tf-modules/vpc-endpoint', 'tf-modules/vpc-endpoint/1.0.0'), + TerraformModule.isModuleAssociatedWithTag('tf-modules/vpc-endpoint', 'tf-modules-vpc-endpoint-v1.0.0'), + ).toBe(true); + expect( + TerraformModule.isModuleAssociatedWithTag('tf-modules/vpc-endpoint', 'tf-modules_vpc_endpoint_v1.0.0'), + ).toBe(true); + expect( + TerraformModule.isModuleAssociatedWithTag('tf-modules/vpc-endpoint', 'tf-modules.vpc.endpoint.v1.0.0'), ).toBe(true); - expect(TerraformModule.isModuleAssociatedWithTag('tf-modules/vpc-endpoint', 'tf-modules/vpc/v1.0.0')).toBe( - false, - ); }); it('should be case sensitive', () => { expect(TerraformModule.isModuleAssociatedWithTag('my-module', 'My-Module/v1.0.0')).toBe(false); expect(TerraformModule.isModuleAssociatedWithTag('my-module', 'my-module/V1.0.0')).toBe(false); }); + + it('should handle tags with different directory separators', () => { + // Test that tags with different separators are properly associated after normalization + expect(TerraformModule.isModuleAssociatedWithTag('my-module', 'my-module/v1.0.0')).toBe(true); + expect(TerraformModule.isModuleAssociatedWithTag('my-module', 'my-module-v1.0.0')).toBe(true); + expect(TerraformModule.isModuleAssociatedWithTag('my-module', 'my-module_v1.0.0')).toBe(true); + expect(TerraformModule.isModuleAssociatedWithTag('my-module', 'my-module.v1.0.0')).toBe(true); + + // Test complex module names with separators + expect( + TerraformModule.isModuleAssociatedWithTag('tf-modules/vpc-endpoint', 'tf-modules/vpc-endpoint/v1.0.0'), + ).toBe(true); + expect( + TerraformModule.isModuleAssociatedWithTag('tf-modules/vpc-endpoint', 'tf-modules-vpc-endpoint-v1.0.0'), + ).toBe(true); + expect( + TerraformModule.isModuleAssociatedWithTag('tf-modules/vpc-endpoint', 'tf-modules_vpc_endpoint_v1.0.0'), + ).toBe(true); + expect( + TerraformModule.isModuleAssociatedWithTag('tf-modules/vpc-endpoint', 'tf-modules.vpc.endpoint.v1.0.0'), + ).toBe(true); + + // Test that wrong associations still return false + expect(TerraformModule.isModuleAssociatedWithTag('my-module', 'other-module/v1.0.0')).toBe(false); + expect(TerraformModule.isModuleAssociatedWithTag('my-module', 'other-module-v1.0.0')).toBe(false); + expect(TerraformModule.isModuleAssociatedWithTag('my-module', 'other-module_v1.0.0')).toBe(false); + expect(TerraformModule.isModuleAssociatedWithTag('my-module', 'other-module.v1.0.0')).toBe(false); + }); }); describe('getTagsForModule()', () => { @@ -1147,6 +1242,45 @@ describe('TerraformModule', () => { expect(tagsToDelete).toEqual(['apple-module/v1.0.0', 'banana-module/v1.0.0', 'zebra-module/v1.0.0']); }); + + it('should handle tags with different directory separators for same module', () => { + // Scenario: Module was originally using / separator, but tags exist with various separators + const allTags = [ + 'test-module/v1.0.0', // Forward slash (current format) + 'test-module-v1.1.0', // Hyphen (old format) + 'test-module_v1.2.0', // Underscore (old format) + 'test-module.v1.3.0', // Dot (old format) + 'other-module/v1.0.0', // Different module that no longer exists + ]; + const existingModules = [createMockTerraformModule({ directory: join(tmpDir, 'test-module') })]; + + const tagsToDelete = TerraformModule.getTagsToDelete(allTags, existingModules); + + // Only tags for non-existent modules should be deleted + // All test-module tags should be kept regardless of separator + expect(tagsToDelete).toEqual(['other-module/v1.0.0']); + }); + + it('should handle complex module names with various separators', () => { + const allTags = [ + 'tf-modules/vpc-endpoint/v1.0.0', // Current format + 'tf-modules-vpc-endpoint-v1.1.0', // All hyphens + 'tf-modules_vpc_endpoint_v1.2.0', // All underscores + 'tf-modules.vpc.endpoint.v1.3.0', // All dots + 'tf-modules/vpc-endpoint-v1.4.0', // Mixed separators + 'removed-module/v1.0.0', // Module that no longer exists + ]; + const existingModules = [ + createMockTerraformModule({ + directory: join(tmpDir, 'tf-modules', 'vpc-endpoint'), + }), + ]; + + const tagsToDelete = TerraformModule.getTagsToDelete(allTags, existingModules); + + // Only tags for non-existent modules should be deleted + expect(tagsToDelete).toEqual(['removed-module/v1.0.0']); + }); }); describe('getReleasesToDelete()', () => { @@ -1536,6 +1670,102 @@ describe('TerraformModule', () => { expect(releasesToDelete).toHaveLength(2); expect(releasesToDelete.map((r) => r.tagName)).toEqual(['legacy-module/v1.0.0', 'legacy-module/v1.2.0']); }); + + it('should handle releases with different directory separators for same module', () => { + // Scenario: Module was originally using / separator, but releases exist with various separators + const allReleases: GitHubRelease[] = [ + { + id: 1, + title: 'test-module/v1.0.0', + tagName: 'test-module/v1.0.0', + body: 'Forward slash format', + }, + { + id: 2, + title: 'test-module-v1.1.0', + tagName: 'test-module-v1.1.0', + body: 'Hyphen format', + }, + { + id: 3, + title: 'test-module_v1.2.0', + tagName: 'test-module_v1.2.0', + body: 'Underscore format', + }, + { + id: 4, + title: 'test-module.v1.3.0', + tagName: 'test-module.v1.3.0', + body: 'Dot format', + }, + { + id: 5, + title: 'other-module/v1.0.0', + tagName: 'other-module/v1.0.0', + body: 'Different module that no longer exists', + }, + ]; + const existingModules = [createMockTerraformModule({ directory: join(tmpDir, 'test-module') })]; + + const releasesToDelete = TerraformModule.getReleasesToDelete(allReleases, existingModules); + + // Only releases for non-existent modules should be deleted + // All test-module releases should be kept regardless of separator + expect(releasesToDelete).toHaveLength(1); + expect(releasesToDelete[0].tagName).toBe('other-module/v1.0.0'); + }); + + it('should handle complex module names with various separators in releases', () => { + const allReleases: GitHubRelease[] = [ + { + id: 1, + title: 'tf-modules/vpc-endpoint/v1.0.0', + tagName: 'tf-modules/vpc-endpoint/v1.0.0', + body: 'Current format', + }, + { + id: 2, + title: 'tf-modules-vpc-endpoint-v1.1.0', + tagName: 'tf-modules-vpc-endpoint-v1.1.0', + body: 'All hyphens', + }, + { + id: 3, + title: 'tf-modules_vpc_endpoint_v1.2.0', + tagName: 'tf-modules_vpc_endpoint_v1.2.0', + body: 'All underscores', + }, + { + id: 4, + title: 'tf-modules.vpc.endpoint.v1.3.0', + tagName: 'tf-modules.vpc.endpoint.v1.3.0', + body: 'All dots', + }, + { + id: 5, + title: 'tf-modules/vpc-endpoint-v1.4.0', + tagName: 'tf-modules/vpc-endpoint-v1.4.0', + body: 'Mixed separators', + }, + { + id: 6, + title: 'removed-module/v1.0.0', + tagName: 'removed-module/v1.0.0', + body: 'Module that no longer exists', + }, + ]; + const existingModules = [ + createMockTerraformModule({ + directory: join(tmpDir, 'tf-modules', 'vpc-endpoint'), + }), + ]; + + const releasesToDelete = TerraformModule.getReleasesToDelete(allReleases, existingModules); + + // Only releases for non-existent modules should be deleted + expect(releasesToDelete).toHaveLength(1); + expect(releasesToDelete[0].tagName).toBe('removed-module/v1.0.0'); + }); }); // Test private helper methods via the public interface diff --git a/__tests__/utils/metadata.test.ts b/__tests__/utils/metadata.test.ts new file mode 100644 index 00000000..8220146c --- /dev/null +++ b/__tests__/utils/metadata.test.ts @@ -0,0 +1,24 @@ +import { createConfigFromInputs } from '@/utils/metadata'; +import { getInput } from '@actions/core'; +import { describe, expect, it, vi } from 'vitest'; + +describe('utils/metadata', () => { + it('should throw a custom error if getInput fails', () => { + const errorMessage = 'Input retrieval failed'; + vi.mocked(getInput).mockImplementation(() => { + throw new Error(errorMessage); + }); + + expect(() => createConfigFromInputs()).toThrow(`Failed to process input 'major-keywords': ${errorMessage}`); + }); + + it('should handle non-Error objects thrown during input processing', () => { + const errorObject = 'A plain string error'; + vi.mocked(getInput).mockImplementation(() => { + // eslint-disable-next-line @typescript-eslint/no-throw-literal + throw errorObject; + }); + + expect(() => createConfigFromInputs()).toThrow(`Failed to process input 'major-keywords': ${String(errorObject)}`); + }); +}); diff --git a/action.yml b/action.yml index 958186ba..1b05d354 100644 --- a/action.yml +++ b/action.yml @@ -122,6 +122,14 @@ inputs: specific requirements. required: true default: ${{ github.token }} + tag-directory-separator: + description: The separator character to use between module directory parts in the git tag. + required: true + default: / + use-version-prefix: + description: Whether to include the 'v' prefix on version tags (e.g., v1.2.3). + required: true + default: "true" outputs: changed-module-names: diff --git a/src/config.ts b/src/config.ts index bc9933f0..cd54879d 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,6 +1,6 @@ import type { Config } from '@/types'; -import { createConfigFromInputs } from '@/utils/action-metadata'; -import { VERSION_TAG_REGEX } from '@/utils/constants'; +import { VALID_TAG_DIRECTORY_SEPARATORS, VERSION_TAG_REGEX } from '@/utils/constants'; +import { createConfigFromInputs } from '@/utils/metadata'; import { endGroup, info, startGroup } from '@actions/core'; // Keep configInstance private to this module @@ -56,13 +56,14 @@ function initializeConfig(): Config { } // Validate tag directory separator - const validSeparators = ['-', '_', '/', '.']; if (configInstance.tagDirectorySeparator.length !== 1) { throw new TypeError('Tag directory separator must be exactly one character'); } - if (!validSeparators.includes(configInstance.tagDirectorySeparator)) { + if (!VALID_TAG_DIRECTORY_SEPARATORS.includes(configInstance.tagDirectorySeparator)) { throw new TypeError( - `Tag directory separator must be one of: ${validSeparators.join(', ')}. Got: '${configInstance.tagDirectorySeparator}'`, + `Tag directory separator must be one of: ${VALID_TAG_DIRECTORY_SEPARATORS.join(', ')}. Got: '${ + configInstance.tagDirectorySeparator + }'`, ); } diff --git a/src/terraform-module.ts b/src/terraform-module.ts index 7da8f36f..77ef079b 100644 --- a/src/terraform-module.ts +++ b/src/terraform-module.ts @@ -2,7 +2,13 @@ import { relative } from 'node:path'; import { config } from '@/config'; import { context } from '@/context'; import type { CommitDetails, GitHubRelease, ReleaseReason, ReleaseType } from '@/types'; -import { MODULE_TAG_REGEX, RELEASE_REASON, RELEASE_TYPE, VERSION_TAG_REGEX } from '@/utils/constants'; +import { + MODULE_TAG_REGEX, + RELEASE_REASON, + RELEASE_TYPE, + VALID_TAG_DIRECTORY_SEPARATORS, + VERSION_TAG_REGEX, +} from '@/utils/constants'; import { removeTrailingCharacters } from '@/utils/string'; import { endGroup, info, startGroup } from '@actions/core'; @@ -213,11 +219,14 @@ export class TerraformModule { * @returns {string | null} The version string including any prefixes (e.g., 'v1.2.3' or '1.2.3'), or null if no tags exist. */ public getLatestTagVersion(): string | null { - if (this.tags.length === 0) { + const latestTag = this.getLatestTag(); + if (latestTag === null) { return null; } - return this.tags[0].replace(`${this.name}/`, ''); + const match = latestTag.match(MODULE_TAG_REGEX); + + return match ? match[3] : null; } /** @@ -452,8 +461,7 @@ export class TerraformModule { semver[2]++; } - // Hard coding "v" for now. Potentially fixing in the future. - return `v${semver.join('.')}`; + return `${config.useVersionPrefix ? 'v' : ''}${semver.join('.')}`; } /** @@ -482,35 +490,43 @@ export class TerraformModule { ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Helper ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** - * Safely extracts the numerical version string from a tag, avoiding regex vulnerabilities. - * Handles tags in format: moduleName/vX.Y.Z or moduleName/X.Y.Z - * Also validates that tag format matches expected pattern and returns only the numerical part. + * Extracts and validates the version string from a Terraform module tag. * - * @param {string} tag - The tag string to extract version from - * @returns {string} The numerical version string (e.g., "1.2.3") - * @throws {Error} If the tag does not match the required format + * Uses the MODULE_TAG_REGEX to validate the tag format and extract version components. + * This method leverages the static validation logic to ensure consistent tag processing + * across different separator formats (/, -, _, .). + * + * @param {string} tag - The tag string to extract version from (e.g., "module/v1.2.3", "module-v1.2.3") + * @returns {string} The numerical version string without prefix (e.g., "1.2.3") + * @throws {Error} If the tag does not match the required format or is not associated with this module */ private extractVersionFromTag(tag: string): string { - // Validate tag format - must start with module name followed by slash - if (!tag.startsWith(`${this.name}/`)) { + // Use the static validation method to ensure the tag is associated with this module + if (!TerraformModule.isModuleAssociatedWithTag(this.name, tag)) { throw new Error( - `Invalid tag format: '${tag}'. Expected format: '${this.name}/v#.#.#' or '${this.name}/#.#.#' for module.`, + `Invalid tag format: '${tag}'. Expected format: '${this.name}[separator]v#.#.#' or '${this.name}[separator]#.#.#'.`, ); } - // Extract everything after the last slash - const versionPart = tag.substring(tag.lastIndexOf('/') + 1); - - // Validate that the version part matches the expected format - if (!VERSION_TAG_REGEX.test(versionPart)) { + // Parse the tag using MODULE_TAG_REGEX to extract version components + // Note: This will never be null since TerraformModule.isModuleAssociatedWithTag already checks for this; + // however, for typing, we'll just recheck. + const match = MODULE_TAG_REGEX.exec(tag); + /* v8 ignore next 5 */ + if (!match) { throw new Error( - `Invalid tag format: '${tag}'. Expected format: '${this.name}/v#.#.#' or '${this.name}/#.#.#' for module.`, + `Invalid tag format: '${tag}'. Expected format: '${this.name}[separator]v#.#.#' or '${this.name}[separator]#.#.#'.`, ); } - // Return only the numerical part, stripping the 'v' prefix if present - return versionPart.startsWith('v') ? versionPart.substring(1) : versionPart; + // Extract the numerical version components (groups 4, 5, 6 are major.minor.patch) + const major = match[4]; + const minor = match[5]; + const patch = match[6]; + + return `${major}.${minor}.${patch}`; } /** @@ -641,23 +657,42 @@ export class TerraformModule { /** * Static utility to check if a tag is associated with a given module name. - * Supports both versioned tags ({moduleName}/v#.#.#) and non-versioned tags ({moduleName}/#.#.#). + * Supports multiple directory separators and handles cases where tagging schemes + * may have changed over time (e.g., from 'module-name/v1.0.0' to 'module-name-v1.1.0'). * - * @param {string} moduleName - The Terraform module name + * @param {string} moduleName - The Terraform module name (assumed to be cleaned) * @param {string} tag - The tag to check * @returns {boolean} True if the tag belongs to the module and has valid version format */ public static isModuleAssociatedWithTag(moduleName: string, tag: string): boolean { - // Check if tag starts with exactly the module name followed by a slash - if (!tag.startsWith(`${moduleName}/`)) { + // Use the existing MODULE_TAG_REGEX to parse the tag and extract module name + version + const match = MODULE_TAG_REGEX.exec(tag); + if (!match) { + // The tag doesn't match the expected "module-name/version" format return false; } - // Extract the version part after the module name and slash - const versionPart = tag.substring(moduleName.length + 1); + // Extract the module name part from the tag (group 1) + const moduleNameFromTag = match[1]; + + // Define a consistent separator to normalize module names + const NORMALIZE_SEPARATOR = '|'; + + // Normalize both the input moduleName and the extracted module name from the tag. + // This allows for comparison even if the tagging scheme changed over time + // (e.g., from 'my/module/v1.0.0' to 'my-module-v1.1.0'). + const normalizeName = (name: string): string => { + // Replace all valid tag directory separators with a consistent separator + // This handles cases where different separators were used in different tags + let normalized = name; + for (const separator of VALID_TAG_DIRECTORY_SEPARATORS) { + normalized = normalized.replaceAll(separator, NORMALIZE_SEPARATOR); + } + return normalized; + }; - // Check if version part matches either v#.#.# or #.#.# format - return VERSION_TAG_REGEX.test(versionPart); + // Compare the normalized names to determine if they match + return normalizeName(moduleName) === normalizeName(moduleNameFromTag); } /** @@ -696,8 +731,9 @@ export class TerraformModule { * Determines an array of Terraform tags that need to be deleted. * * Identifies tags that belong to modules no longer present in the current - * module list by filtering tags that match the pattern {moduleName}/vX.Y.Z - * where the module name is not in the current modules. + * module list by checking if any current module is associated with each tag. + * This approach leverages the robust tag association logic that handles + * different separator schemes over time. * * @param {string[]} allTags - A list of all tags associated with the modules. * @param {TerraformModule[]} terraformModules - An array of Terraform modules. @@ -706,16 +742,12 @@ export class TerraformModule { public static getTagsToDelete(allTags: string[], terraformModules: TerraformModule[]): string[] { startGroup('Finding all Terraform tags that should be deleted'); - // Get module names from current terraformModules (these exist in source) - const moduleNamesFromModules = new Set(terraformModules.map((module) => module.name)); - - // Filter tags that belong to modules no longer in the current module list + // Filter tags that are not associated with any current module const tagsToRemove = allTags .filter((tag) => { - // Extract the Terraform module name from tag by removing the version suffix - const match = MODULE_TAG_REGEX.exec(tag); - const moduleName = match ? match[1] : tag; - return !moduleNamesFromModules.has(moduleName); + // Check if ANY current module is associated with this tag + // This handles cases where tagging schemes changed over time + return !terraformModules.some((module) => TerraformModule.isModuleAssociatedWithTag(module.name, tag)); }) .sort((a, b) => a.localeCompare(b)); @@ -731,8 +763,9 @@ export class TerraformModule { * Determines an array of Terraform releases that need to be deleted. * * Identifies releases that belong to modules no longer present in the current - * module list by filtering releases that match the pattern {moduleName}/vX.Y.Z - * where the module name is not in the current modules. + * module list by checking if any current module is associated with each release tag. + * This approach leverages the robust tag association logic that handles + * different separator schemes over time. * * @param {GitHubRelease[]} allReleases - A list of all releases associated with the modules. * @param {TerraformModule[]} terraformModules - An array of Terraform modules. @@ -749,16 +782,14 @@ export class TerraformModule { ): GitHubRelease[] { startGroup('Finding all Terraform releases that should be deleted'); - // Get module names from current terraformModules (these exist in source) - const moduleNamesFromModules = new Set(terraformModules.map((module) => module.name)); - - // Filter releases that belong to modules no longer in the current module list + // Filter releases that are not associated with any current module const releasesToRemove = allReleases .filter((release) => { - // Extract module name from versioned release tag - const match = MODULE_TAG_REGEX.exec(release.tagName); - const moduleName = match ? match[1] : release.tagName; - return !moduleNamesFromModules.has(moduleName); + // Check if ANY current module is associated with this release tag + // This handles cases where tagging schemes changed over time + return !terraformModules.some((module) => + TerraformModule.isModuleAssociatedWithTag(module.name, release.tagName), + ); }) .sort((a, b) => a.tagName.localeCompare(b.tagName)); diff --git a/src/types/config.types.ts b/src/types/config.types.ts index 9d726d42..72086d41 100644 --- a/src/types/config.types.ts +++ b/src/types/config.types.ts @@ -26,7 +26,8 @@ export interface Config { /** * Default first tag for initializing repositories without existing tags. - * This serves as the fallback tag when no tags are found in the repository. + * This serves as the fallback tag when no tags are found in the repository. Note this may + * be in the format of `v#.#.#` or `#.#.#` (e.g., `v1.0.0` or `1.0.0`). */ defaultFirstTag: string; diff --git a/src/types/index.ts b/src/types/index.ts index ca0629de..dd55ef10 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,5 @@ // Action metadata types -export * from './action-metadata.types'; +export * from './metadata.types'; // Common types export * from './common.types'; diff --git a/src/types/action-metadata.types.ts b/src/types/metadata.types.ts similarity index 100% rename from src/types/action-metadata.types.ts rename to src/types/metadata.types.ts diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 922f45dc..630f664d 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1,3 +1,19 @@ +/** + * Defines valid separator characters for tag directory paths in Terraform module releases. + * + * When finding a Terraform module like `modules/aws/s3-bucket`, the release tag would typically be + * `modules/aws/s3-bucket/v1.0.0`. This constant allows for alternative separators in the tag path. + * + * For example, with these separators, the following tag formats would all be valid: + * - `modules/aws/s3-bucket/v1.0.0` (using '/') + * - `modules-aws-s3-bucket-v1.0.0` (using '-') + * - `modules_aws_s3_bucket_v1.0.0` (using '_') + * - `modules.aws.s3.bucket.v1.0.0` (using '.') + * + * The default separator is '/' as defined in action.yml.dddd + */ +export const VALID_TAG_DIRECTORY_SEPARATORS = ['-', '_', '/', '.']; + /** * Regular expression that matches version tags in the format of semantic versioning. * This regex validates version strings like "1.2.3" or "v1.2.3" and includes capture groups. @@ -8,13 +24,28 @@ * It allows either a numerical portion (e.g., "1.2.3") or one prefixed with 'v' (e.g., "v1.2.3"), * which is the proper semver default format. */ -export const VERSION_TAG_REGEX = /^v?(\d+)\.(\d+)\.(\d+)$/; +export const VERSION_TAG_REGEX = /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/; /** * Matches a Terraform module tag in the format: module-name/v1.2.3 or module-name/1.2.3 * Group 1: module name, Group 2: version (with or without 'v' prefix) */ -export const MODULE_TAG_REGEX = /^(.+)\/(v?\d+\.\d+\.\d+)$/; +/** + * Regular expression pattern to match a module tag in the format: prefix + separator + version + * Where: + * - Group 1: prefix (e.g., "module", "feature") + * - Group 2: separator (one of: '-', '_', '/', '.') + * - Group 3: Complete version string with optional 'v' prefix (e.g., "v1.0.0", "1.0.0") + * - Group 4: Major version number + * - Group 5: Minor version number + * - Group 6: Patch version number + * + * Example matches: + * - "module-v1.0.0" → ["module-v1.0.0", "module", "-", "v1.0.0", "1", "0", "0"] + * - "feature_2.3.4" → ["feature_2.3.4", "feature", "_", "2.3.4", "2", "3", "4"] + * - "service/v0.1.0" → ["service/v0.1.0", "service", "/", "v0.1.0", "0", "1", "0"] + */ +export const MODULE_TAG_REGEX = /^(.+)([-_\/\.])(v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*))$/; /** * Release type constants for semantic versioning diff --git a/src/utils/action-metadata.ts b/src/utils/metadata.ts similarity index 100% rename from src/utils/action-metadata.ts rename to src/utils/metadata.ts From 6dff8a54b33d77065e68c9b1c90fd08747bae2b2 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Thu, 19 Jun 2025 19:48:20 +0000 Subject: [PATCH 15/29] fix: update rollup packages to version 4.44.0 for improved compatibility --- package-lock.json | 175 ++++++++++++++++++++++------------------------ 1 file changed, 84 insertions(+), 91 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3bfc3c8f..af3108ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1047,9 +1047,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.43.0.tgz", - "integrity": "sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.0.tgz", + "integrity": "sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==", "cpu": [ "arm" ], @@ -1061,9 +1061,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.43.0.tgz", - "integrity": "sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.0.tgz", + "integrity": "sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==", "cpu": [ "arm64" ], @@ -1075,9 +1075,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.43.0.tgz", - "integrity": "sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.0.tgz", + "integrity": "sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==", "cpu": [ "arm64" ], @@ -1089,9 +1089,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.43.0.tgz", - "integrity": "sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.0.tgz", + "integrity": "sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==", "cpu": [ "x64" ], @@ -1103,9 +1103,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.43.0.tgz", - "integrity": "sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.0.tgz", + "integrity": "sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==", "cpu": [ "arm64" ], @@ -1117,9 +1117,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.43.0.tgz", - "integrity": "sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.0.tgz", + "integrity": "sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==", "cpu": [ "x64" ], @@ -1131,9 +1131,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.43.0.tgz", - "integrity": "sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.0.tgz", + "integrity": "sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==", "cpu": [ "arm" ], @@ -1145,9 +1145,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.43.0.tgz", - "integrity": "sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.0.tgz", + "integrity": "sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==", "cpu": [ "arm" ], @@ -1159,9 +1159,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.43.0.tgz", - "integrity": "sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.0.tgz", + "integrity": "sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==", "cpu": [ "arm64" ], @@ -1173,9 +1173,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.43.0.tgz", - "integrity": "sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.0.tgz", + "integrity": "sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==", "cpu": [ "arm64" ], @@ -1187,9 +1187,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.43.0.tgz", - "integrity": "sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.0.tgz", + "integrity": "sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==", "cpu": [ "loong64" ], @@ -1201,9 +1201,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.43.0.tgz", - "integrity": "sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.0.tgz", + "integrity": "sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==", "cpu": [ "ppc64" ], @@ -1215,9 +1215,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.43.0.tgz", - "integrity": "sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.0.tgz", + "integrity": "sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==", "cpu": [ "riscv64" ], @@ -1229,9 +1229,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.43.0.tgz", - "integrity": "sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.0.tgz", + "integrity": "sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==", "cpu": [ "riscv64" ], @@ -1243,9 +1243,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.43.0.tgz", - "integrity": "sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.0.tgz", + "integrity": "sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==", "cpu": [ "s390x" ], @@ -1257,9 +1257,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.43.0.tgz", - "integrity": "sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.0.tgz", + "integrity": "sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==", "cpu": [ "x64" ], @@ -1271,9 +1271,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.43.0.tgz", - "integrity": "sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.0.tgz", + "integrity": "sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==", "cpu": [ "x64" ], @@ -1285,9 +1285,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.43.0.tgz", - "integrity": "sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.0.tgz", + "integrity": "sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==", "cpu": [ "arm64" ], @@ -1299,9 +1299,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.43.0.tgz", - "integrity": "sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.0.tgz", + "integrity": "sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==", "cpu": [ "ia32" ], @@ -1313,9 +1313,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.43.0.tgz", - "integrity": "sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.0.tgz", + "integrity": "sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==", "cpu": [ "x64" ], @@ -4756,13 +4756,13 @@ } }, "node_modules/rollup": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.43.0.tgz", - "integrity": "sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.0.tgz", + "integrity": "sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.7" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -4772,36 +4772,29 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.43.0", - "@rollup/rollup-android-arm64": "4.43.0", - "@rollup/rollup-darwin-arm64": "4.43.0", - "@rollup/rollup-darwin-x64": "4.43.0", - "@rollup/rollup-freebsd-arm64": "4.43.0", - "@rollup/rollup-freebsd-x64": "4.43.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.43.0", - "@rollup/rollup-linux-arm-musleabihf": "4.43.0", - "@rollup/rollup-linux-arm64-gnu": "4.43.0", - "@rollup/rollup-linux-arm64-musl": "4.43.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.43.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.43.0", - "@rollup/rollup-linux-riscv64-gnu": "4.43.0", - "@rollup/rollup-linux-riscv64-musl": "4.43.0", - "@rollup/rollup-linux-s390x-gnu": "4.43.0", - "@rollup/rollup-linux-x64-gnu": "4.43.0", - "@rollup/rollup-linux-x64-musl": "4.43.0", - "@rollup/rollup-win32-arm64-msvc": "4.43.0", - "@rollup/rollup-win32-ia32-msvc": "4.43.0", - "@rollup/rollup-win32-x64-msvc": "4.43.0", + "@rollup/rollup-android-arm-eabi": "4.44.0", + "@rollup/rollup-android-arm64": "4.44.0", + "@rollup/rollup-darwin-arm64": "4.44.0", + "@rollup/rollup-darwin-x64": "4.44.0", + "@rollup/rollup-freebsd-arm64": "4.44.0", + "@rollup/rollup-freebsd-x64": "4.44.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.44.0", + "@rollup/rollup-linux-arm-musleabihf": "4.44.0", + "@rollup/rollup-linux-arm64-gnu": "4.44.0", + "@rollup/rollup-linux-arm64-musl": "4.44.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.44.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.44.0", + "@rollup/rollup-linux-riscv64-gnu": "4.44.0", + "@rollup/rollup-linux-riscv64-musl": "4.44.0", + "@rollup/rollup-linux-s390x-gnu": "4.44.0", + "@rollup/rollup-linux-x64-gnu": "4.44.0", + "@rollup/rollup-linux-x64-musl": "4.44.0", + "@rollup/rollup-win32-arm64-msvc": "4.44.0", + "@rollup/rollup-win32-ia32-msvc": "4.44.0", + "@rollup/rollup-win32-x64-msvc": "4.44.0", "fsevents": "~2.3.2" } }, - "node_modules/rollup/node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, - "license": "MIT" - }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", From 26b7e799516969b785cfb95436d21d8a50a3d4a5 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Thu, 19 Jun 2025 19:50:00 +0000 Subject: [PATCH 16/29] feat: enhance tag generation configuration with detailed descriptions and examples for directory separator and version prefix options --- .github/workflows/ci.yml | 2 + .github/workflows/test.yml | 24 ++- README.md | 4 +- __tests__/helpers/octokit.ts | 5 +- __tests__/terraform-module.test.ts | 234 +++++++++++++++++++++++++---- __tests__/utils/string.test.ts | 66 +++++--- action.yml | 17 ++- src/config.ts | 7 +- src/terraform-module.ts | 37 +++-- src/types/config.types.ts | 33 +++- src/types/metadata.types.ts | 35 ++++- src/utils/constants.ts | 6 +- src/utils/string.ts | 62 ++++---- 13 files changed, 406 insertions(+), 126 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 948c0003..9524be92 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,6 +54,8 @@ jobs: module-change-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/**,examples/** module-asset-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/** use-ssh-source-format: true + tag-directory-separator: "-" + use-version-prefix: false - name: Test Action Outputs id: test-outputs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f3d077b9..9f0491d8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,9 +10,9 @@ on: types: [opened, synchronize, reopened] jobs: - tests: + typescript-tests: runs-on: ubuntu-latest - name: Test + name: TypeScript Tests permissions: contents: read steps: @@ -35,6 +35,26 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN_REPO_CI_TESTING }} + sonarqube-scan: + runs-on: ubuntu-latest + name: SonarQube Analysis + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + with: + # Disabling shallow clone is recommended for improving relevancy of reporting + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: .node-version + cache: npm + + - name: Install Dependencies + run: npm ci --no-fund + - name: SonarQube Scan uses: SonarSource/sonarqube-scan-action@2500896589ef8f7247069a56136f8dc177c27ccf # v5 env: diff --git a/README.md b/README.md index 16227690..44577c50 100644 --- a/README.md +++ b/README.md @@ -194,12 +194,14 @@ configuring the following optional input parameters as needed. | `delete-legacy-tags` | Specifies a boolean that determines whether tags and releases from Terraform modules that have been deleted should be automatically removed | `true` | | `disable-wiki` | Whether to disable wiki generation for Terraform modules | `false` | | `wiki-sidebar-changelog-max` | An integer that specifies how many changelog entries are displayed in the sidebar per module | `5` | +| `wiki-usage-template` | A raw, multi-line string to override the default 'Usage' section in the generated wiki. Allows using variables like {{module_name}}, {{latest_tag}}, {{latest_tag_version_number}} and more.
[Read more here](#configuring-the-usage-template) | [See action.yml](https://github.com/polleuretan/terraform-module-releaser/blob/main/action.yml#L108) | | `disable-branding` | Controls whether a small branding link to the action's repository is added to PR comments. Recommended to leave enabled to support OSS. | `false` | | `module-path-ignore` | Comma-separated list of module paths to completely ignore. Modules matching any pattern here are excluded from all versioning, releases, and documentation.
[Read more here](#understanding-the-filtering-options) | `` (empty string) | | `module-change-exclude-patterns` | Comma-separated list of file patterns (relative to each module) to exclude from triggering version changes. Lets you release a module but control which files inside it do not force a version bump.
[Read more here](#understanding-the-filtering-options) | `.gitignore,*.md,*.tftest.hcl,tests/**` | | `module-asset-exclude-patterns` | A comma-separated list of file patterns to exclude when bundling a Terraform module for tag/release. Patterns follow glob syntax (e.g., `tests/\*\*`) and are relative to each Terraform module directory. Files matching these patterns will be excluded from the bundled output. | `.gitignore,*.md,*.tftest.hcl,tests/**` | | `use-ssh-source-format` | If enabled, all links to source code in generated Wiki documentation will use SSH standard format (e.g., `git::ssh://git@github.com/owner/repo.git`) instead of HTTPS format (`git::https://github.com/owner/repo.git`) | `false` | -| `wiki-usage-template` | A raw, multi-line string to override the default 'Usage' section in the generated wiki. Allows using variables like {{module_name}}, {{latest_tag}}, {{latest_tag_version_number}} and more.
[Read more here](#configuring-the-usage-template) | [See action.yml](https://github.com/polleuretan/terraform-module-releaser/blob/main/action.yml#L108) | +| `tag-directory-separator` | Character used to separate directory path components in Git tags. Supports `/`, `-`, `_`, or `.` | `/` | +| `use-version-prefix` | Whether to include the 'v' prefix on version tags (e.g., v1.2.3 vs 1.2.3) | `true` | ### Understanding the filtering options diff --git a/__tests__/helpers/octokit.ts b/__tests__/helpers/octokit.ts index 4438055f..e5cb69aa 100644 --- a/__tests__/helpers/octokit.ts +++ b/__tests__/helpers/octokit.ts @@ -1,5 +1,5 @@ import type { OctokitRestApi } from '@/types'; -import { trimSlashes } from '@/utils/string'; +import { removeTrailingCharacters } from '@/utils/string'; import { paginateRest } from '@octokit/plugin-paginate-rest'; import { restEndpointMethods } from '@octokit/plugin-rest-endpoint-methods'; import type { RestEndpointMethodTypes } from '@octokit/plugin-rest-endpoint-methods'; @@ -384,5 +384,6 @@ function getLinkHeader(slug: string, page: number, perPage: number, totalCount: const nextPage = page + 1; const lastPage = totalPages; - return `; rel="next", ; rel="last"`; + const slugTrimmed = removeTrailingCharacters(slug, ['/']); + return `; rel="next", ; rel="last"`; } diff --git a/__tests__/terraform-module.test.ts b/__tests__/terraform-module.test.ts index e99cdcf5..7be18aaa 100644 --- a/__tests__/terraform-module.test.ts +++ b/__tests__/terraform-module.test.ts @@ -58,7 +58,7 @@ describe('TerraformModule', () => { mkdirSync(specialDir, { recursive: true }); const module = new TerraformModule(specialDir); - expect(module.name).toBe('complex_module-name-with/chars'); + expect(module.name).toBe('complex_module-name.with/chars'); }); it('should handle nested directory paths', () => { @@ -949,44 +949,222 @@ describe('TerraformModule', () => { describe('static utilities', () => { describe('getTerraformModuleNameFromRelativePath()', () => { - it('should generate valid module names from paths', () => { - expect(TerraformModule.getTerraformModuleNameFromRelativePath('tf-modules/simple-module')).toBe( - 'tf-modules/simple-module', - ); + beforeEach(() => { + // Reset to default config for each test + config.set({ + tagDirectorySeparator: '/', + majorKeywords: ['BREAKING CHANGE', 'major change'], + minorKeywords: ['feat:', 'feature:'], + defaultFirstTag: 'v0.1.0', + moduleChangeExcludePatterns: [], + modulePathIgnore: [], + useVersionPrefix: true, + }); + }); - expect(TerraformModule.getTerraformModuleNameFromRelativePath('complex_module-name.with/chars')).toBe( - 'complex_module-name-with/chars', - ); + describe('with different tag directory separators', () => { + it('should use forward slash separator by default', () => { + config.set({ tagDirectorySeparator: '/' }); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('tf-modules/simple-module')).toBe( + 'tf-modules/simple-module', + ); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('complex\\module\\windows\\path')).toBe( + 'complex/module/windows/path', + ); + }); - expect(TerraformModule.getTerraformModuleNameFromRelativePath('/leading/slash/')).toBe('leading/slash'); + it('should use hyphen separator when configured', () => { + config.set({ tagDirectorySeparator: '-' }); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('tf-modules/simple-module')).toBe( + 'tf-modules-simple-module', + ); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('complex\\module\\windows\\path')).toBe( + 'complex-module-windows-path', + ); + }); - expect(TerraformModule.getTerraformModuleNameFromRelativePath('module...with...dots')).toBe('module-with-dots'); - }); + it('should use underscore separator when configured', () => { + config.set({ tagDirectorySeparator: '_' }); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('tf-modules/simple-module')).toBe( + 'tf-modules_simple-module', + ); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('complex\\module\\windows\\path')).toBe( + 'complex_module_windows_path', + ); + }); - it('should handle leading and trailing slashes', () => { - expect(TerraformModule.getTerraformModuleNameFromRelativePath('/test-module/')).toBe('test-module'); + it('should use dot separator when configured', () => { + config.set({ tagDirectorySeparator: '.' }); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('tf-modules/simple-module')).toBe( + 'tf-modules.simple-module', + ); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('complex\\module\\windows\\path')).toBe( + 'complex.module.windows.path', + ); + }); }); - it('should handle multiple consecutive slashes', () => { - expect(TerraformModule.getTerraformModuleNameFromRelativePath('tf-modules//vpc//endpoint')).toBe( - 'tf-modules/vpc/endpoint', - ); - }); + describe('character normalization and cleanup', () => { + beforeEach(() => { + config.set({ tagDirectorySeparator: '/' }); + }); - it('should handle whitespace', () => { - expect(TerraformModule.getTerraformModuleNameFromRelativePath(' test module ')).toBe('test-module'); - }); + it('should normalize Windows backslashes to configured separator', () => { + expect(TerraformModule.getTerraformModuleNameFromRelativePath('windows\\path\\module')).toBe( + 'windows/path/module', + ); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('mixed\\and/path\\separators')).toBe( + 'mixed/and/path/separators', + ); + }); - it('should convert to lowercase', () => { - expect(TerraformModule.getTerraformModuleNameFromRelativePath('Test-Module')).toBe('test-module'); - }); + it('should convert to lowercase', () => { + expect(TerraformModule.getTerraformModuleNameFromRelativePath('Test-Module')).toBe('test-module'); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('UPPERCASE/MODULE')).toBe('uppercase/module'); + }); + + it('should replace invalid characters with hyphens', () => { + expect(TerraformModule.getTerraformModuleNameFromRelativePath('test@module!#$')).toBe('test-module'); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('module%with&special*chars')).toBe( + 'module-with-special-chars', + ); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('test module with spaces')).toBe( + 'test-module-with-spaces', + ); + }); + + it('should normalize consecutive special characters', () => { + expect(TerraformModule.getTerraformModuleNameFromRelativePath('module...with...dots')).toBe( + 'module.with.dots', + ); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('tf-modules//vpc//endpoint')).toBe( + 'tf-modules/vpc/endpoint', + ); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('module---with---hyphens')).toBe( + 'module-with-hyphens', + ); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('module___with___underscores')).toBe( + 'module_with_underscores', + ); + }); + + it('should remove leading and trailing special characters', () => { + expect(TerraformModule.getTerraformModuleNameFromRelativePath('/leading/slash/')).toBe('leading/slash'); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('...leading.dots')).toBe('leading.dots'); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('trailing.dots...')).toBe('trailing.dots'); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('---leading-hyphens')).toBe('leading-hyphens'); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('trailing-hyphens---')).toBe( + 'trailing-hyphens', + ); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('___leading_underscores')).toBe( + 'leading_underscores', + ); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('trailing_underscores___')).toBe( + 'trailing_underscores', + ); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('/.-_mixed_leading')).toBe('mixed_leading'); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('mixed_trailing_.-/')).toBe('mixed_trailing'); + }); - it('should clean up invalid characters', () => { - expect(TerraformModule.getTerraformModuleNameFromRelativePath('test@module!#$')).toBe('test-module'); + it('should handle edge cases', () => { + expect(TerraformModule.getTerraformModuleNameFromRelativePath(' whitespace ')).toBe('whitespace'); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('/test-module/')).toBe('test-module'); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('single')).toBe('single'); + expect(TerraformModule.getTerraformModuleNameFromRelativePath('a')).toBe('a'); + }); }); - it('should remove trailing special characters', () => { - expect(TerraformModule.getTerraformModuleNameFromRelativePath('test-module-.')).toBe('test-module'); + describe('comprehensive scenarios with different separators', () => { + const testScenarios = [ + { + separator: '/', + input: 'tf-modules/aws/vpc-endpoint', + expected: 'tf-modules/aws/vpc-endpoint', + }, + { + separator: '-', + input: 'tf-modules/aws/vpc-endpoint', + expected: 'tf-modules-aws-vpc-endpoint', + }, + { + separator: '_', + input: 'tf-modules/aws/vpc-endpoint', + expected: 'tf-modules_aws_vpc-endpoint', + }, + { + separator: '.', + input: 'tf-modules/aws/vpc-endpoint', + expected: 'tf-modules.aws.vpc-endpoint', + }, + ]; + + for (const { separator, input, expected } of testScenarios) { + it(`should handle complex paths with ${separator} separator`, () => { + config.set({ tagDirectorySeparator: separator }); + expect(TerraformModule.getTerraformModuleNameFromRelativePath(input)).toBe(expected); + }); + } + + const complexTestScenarios = [ + { + separator: '/', + input: '//tf-modules//aws..vpc--endpoint__', + expected: 'tf-modules/aws.vpc-endpoint', + }, + { + separator: '-', + input: '//tf-modules//aws..vpc--endpoint__', + expected: 'tf-modules-aws.vpc-endpoint', + }, + { + separator: '_', + input: '//tf-modules//aws..vpc--endpoint__', + expected: 'tf-modules_aws.vpc-endpoint', + }, + { + separator: '.', + input: '//tf-modules//aws..vpc--endpoint__', + expected: 'tf-modules.aws.vpc-endpoint', + }, + ]; + + for (const { separator, input, expected } of complexTestScenarios) { + it(`should handle complex normalization with ${separator} separator`, () => { + config.set({ tagDirectorySeparator: separator }); + expect(TerraformModule.getTerraformModuleNameFromRelativePath(input)).toBe(expected); + }); + } + }); + + describe('real-world terraform module scenarios', () => { + it('should handle typical terraform module paths', () => { + config.set({ tagDirectorySeparator: '/' }); + + const testCases = [ + { input: 'modules/networking/vpc', expected: 'modules/networking/vpc' }, + { input: 'modules/compute/ec2-instance', expected: 'modules/compute/ec2-instance' }, + { input: 'modules/storage/s3-bucket', expected: 'modules/storage/s3-bucket' }, + { input: 'terraform/aws/rds_cluster', expected: 'terraform/aws/rds_cluster' }, + { input: 'tf-modules/azure/storage.account', expected: 'tf-modules/azure/storage.account' }, + ]; + + for (const { input, expected } of testCases) { + expect(TerraformModule.getTerraformModuleNameFromRelativePath(input)).toBe(expected); + } + }); + + it('should handle module paths with various separators configured', () => { + const separatorTests = [ + { separator: '-', input: 'modules/aws/vpc', expected: 'modules-aws-vpc' }, + { separator: '_', input: 'modules/aws/vpc', expected: 'modules_aws_vpc' }, + { separator: '.', input: 'modules/aws/vpc', expected: 'modules.aws.vpc' }, + ]; + + for (const { separator, input, expected } of separatorTests) { + config.set({ tagDirectorySeparator: separator }); + expect(TerraformModule.getTerraformModuleNameFromRelativePath(input)).toBe(expected); + } + }); }); }); diff --git a/__tests__/utils/string.test.ts b/__tests__/utils/string.test.ts index b2ce45d7..5c681789 100644 --- a/__tests__/utils/string.test.ts +++ b/__tests__/utils/string.test.ts @@ -1,36 +1,49 @@ -import { removeTrailingCharacters, trimSlashes } from '@/utils/string'; +import { removeLeadingCharacters, removeTrailingCharacters } from '@/utils/string'; import { describe, expect, it } from 'vitest'; describe('utils/string', () => { - describe('trimSlashes', () => { - it('should remove leading and trailing slashes while preserving internal ones', () => { - const testCases = [ - { input: '/example/path/', expected: 'example/path' }, - { input: '///another/example///', expected: 'another/example' }, - { input: 'no/slashes', expected: 'no/slashes' }, - { input: '/', expected: '' }, - { input: '//', expected: '' }, - { input: '', expected: '' }, - { input: '/single/', expected: 'single' }, - { input: 'leading/', expected: 'leading' }, - { input: '/trailing', expected: 'trailing' }, - { input: '////multiple////slashes////', expected: 'multiple////slashes' }, - ]; - for (const { input, expected } of testCases) { - expect(trimSlashes(input)).toBe(expected); - } + describe('removeLeadingCharacters', () => { + it('should remove leading dots', () => { + expect(removeLeadingCharacters('...hello', ['.'])).toBe('hello'); + expect(removeLeadingCharacters('..module-name', ['.'])).toBe('module-name'); + expect(removeLeadingCharacters('.....test', ['.'])).toBe('test'); }); - it('should handle strings without any slashes', () => { - expect(trimSlashes('hello')).toBe('hello'); + it('should remove leading hyphens and underscores', () => { + expect(removeLeadingCharacters('--module-name', ['-'])).toBe('module-name'); + expect(removeLeadingCharacters('__module_name', ['_'])).toBe('module_name'); + expect(removeLeadingCharacters('-_module-name', ['-', '_'])).toBe('module-name'); }); - it('should return empty string when given only slashes', () => { - expect(trimSlashes('//////')).toBe(''); + it('should remove multiple leading character types', () => { + expect(removeLeadingCharacters('._-module-name', ['.', '-', '_'])).toBe('module-name'); + expect(removeLeadingCharacters('.--__test', ['.', '-', '_'])).toBe('test'); + expect(removeLeadingCharacters('___...---example', ['.', '-', '_'])).toBe('example'); }); - it('should preserve internal multiple slashes', () => { - expect(trimSlashes('/path//with///internal////slashes/')).toBe('path//with///internal////slashes'); + it('should preserve internal characters', () => { + expect(removeLeadingCharacters('.hello.world', ['.'])).toBe('hello.world'); + expect(removeLeadingCharacters('.-module-name.test', ['.', '-'])).toBe('module-name.test'); + expect(removeLeadingCharacters('_test_module_name', ['_'])).toBe('test_module_name'); + }); + + it('should handle edge cases', () => { + expect(removeLeadingCharacters('', ['.'])).toBe(''); + expect(removeLeadingCharacters('...', ['.'])).toBe(''); + expect(removeLeadingCharacters('---', ['-'])).toBe(''); + expect(removeLeadingCharacters('hello', ['.', '-', '_'])).toBe('hello'); + expect(removeLeadingCharacters('module', [])).toBe('module'); + }); + + it('should handle complex terraform module names', () => { + expect(removeLeadingCharacters('._-aws-vpc-module', ['.', '-', '_'])).toBe('aws-vpc-module'); + expect(removeLeadingCharacters('--tf-modules/vpc-endpoint', ['-', '_'])).toBe('tf-modules/vpc-endpoint'); + expect(removeLeadingCharacters('__modules/networking/vpc', ['_'])).toBe('modules/networking/vpc'); + }); + + it('should handle forward slashes in leading characters', () => { + expect(removeLeadingCharacters('/./module-name', ['/', '.'])).toBe('module-name'); + expect(removeLeadingCharacters('/./_-example', ['/', '.', '_', '-'])).toBe('example'); }); }); @@ -72,5 +85,10 @@ describe('utils/string', () => { expect(removeTrailingCharacters('tf-modules/vpc-endpoint--', ['-', '_'])).toBe('tf-modules/vpc-endpoint'); expect(removeTrailingCharacters('modules/networking/vpc__', ['_'])).toBe('modules/networking/vpc'); }); + + it('should handle forward slashes in trailing characters', () => { + expect(removeTrailingCharacters('module-name/.', ['/', '.'])).toBe('module-name'); + expect(removeTrailingCharacters('example-_./', ['/', '.', '_', '-'])).toBe('example'); + }); }); }); diff --git a/action.yml b/action.yml index 1b05d354..bcf19c22 100644 --- a/action.yml +++ b/action.yml @@ -123,11 +123,24 @@ inputs: required: true default: ${{ github.token }} tag-directory-separator: - description: The separator character to use between module directory parts in the git tag. + description: > + Character used to separate directory path components in Git tags. This separator is used to convert + module directory paths into tag names (e.g., 'modules/aws/s3-bucket' becomes 'modules-aws-s3-bucket-v1.0.0' + when using '-'). Must be a single character from: /, -, _, or . + + Examples with different separators: + - "/" (default): modules/aws/s3-bucket/v1.0.0 + - "-": modules-aws-s3-bucket-v1.0.0 + - "_": modules_aws_s3_bucket_v1.0.0 + - ".": modules.aws.s3.bucket.v1.0.0 required: true default: / use-version-prefix: - description: Whether to include the 'v' prefix on version tags (e.g., v1.2.3). + description: > + Whether to include the 'v' prefix on version tags (e.g., v1.2.3 vs 1.2.3). When enabled, all new version + tags will include the 'v' prefix. For initial releases, this setting takes precedence over any 'v' prefix + specified in the default-first-tag - if use-version-prefix is false and default-first-tag contains 'v', + the 'v' will be automatically removed to ensure consistency. required: true default: "true" diff --git a/src/config.ts b/src/config.ts index cd54879d..e116d9fc 100644 --- a/src/config.ts +++ b/src/config.ts @@ -66,7 +66,6 @@ function initializeConfig(): Config { }'`, ); } - // Validate default first tag format if (!VERSION_TAG_REGEX.test(configInstance.defaultFirstTag)) { throw new TypeError( @@ -74,6 +73,12 @@ function initializeConfig(): Config { ); } + // If we aren't using "v" prefix but the default first tag was specified with a "v" + // prefix, then strip this to enforce. + if (!configInstance.useVersionPrefix && configInstance.defaultFirstTag.startsWith('v')) { + configInstance.defaultFirstTag = configInstance.defaultFirstTag.substring(1); + } + info(`Major Keywords: ${configInstance.majorKeywords.join(', ')}`); info(`Minor Keywords: ${configInstance.minorKeywords.join(', ')}`); info(`Patch Keywords: ${configInstance.patchKeywords.join(', ')}`); diff --git a/src/terraform-module.ts b/src/terraform-module.ts index 77ef079b..c18d017b 100644 --- a/src/terraform-module.ts +++ b/src/terraform-module.ts @@ -9,7 +9,7 @@ import { VALID_TAG_DIRECTORY_SEPARATORS, VERSION_TAG_REGEX, } from '@/utils/constants'; -import { removeTrailingCharacters } from '@/utils/string'; +import { removeLeadingCharacters, removeTrailingCharacters } from '@/utils/string'; import { endGroup, info, startGroup } from '@actions/core'; /** @@ -224,7 +224,7 @@ export class TerraformModule { return null; } - const match = latestTag.match(MODULE_TAG_REGEX); + const match = MODULE_TAG_REGEX.exec(latestTag); return match ? match[3] : null; } @@ -484,7 +484,7 @@ export class TerraformModule { return null; } - return `${this.name}/${releaseTagVersion}`; + return `${this.name}${config.tagDirectorySeparator}${releaseTagVersion}`; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -630,29 +630,28 @@ export class TerraformModule { * * The function transforms the directory path by: * - Trimming whitespace - * - Replacing invalid characters with hyphens - * - Normalizing slashes - * - Removing leading/trailing slashes - * - Handling consecutive dots and hyphens - * - Removing any remaining whitespace * - Converting to lowercase (for consistency) - * - Removing trailing dots, hyphens, and underscores + * - Normalizing path separators (both backslashes and forward slashes) to the configured tag directory separator + * - Replacing invalid characters with hyphens (preserving only alphanumeric, "/", ".", "-", "_") + * - Normalizing consecutive special characters ("/", ".", "-", "_") to single instances + * - Removing leading/trailing special characters ("/", ".", "-", "_") using safe string operations * * @param {string} terraformDirectory - The relative directory path from which to generate the module name. * @returns {string} A valid Terraform module name based on the provided directory path. */ public static getTerraformModuleNameFromRelativePath(terraformDirectory: string): string { - const cleanedDirectory = terraformDirectory + let name = terraformDirectory .trim() - .replace(/[^a-zA-Z0-9/_-]+/g, '-') - .replace(/\/{2,}/g, '/') - .replace(/\/\.+/g, '/') - .replace(/(^\/|\/$)/g, '') - .replace(/\.\.+/g, '.') - .replace(/--+/g, '-') - .replace(/\s+/g, '') - .toLowerCase(); - return removeTrailingCharacters(cleanedDirectory, ['.', '-', '_']); + .toLowerCase() + .replace(/[/\\]/g, config.tagDirectorySeparator) // Normalize backslashes and forward slashes to configured separator + .replace(/[^a-zA-Z0-9/._-]+/g, '-') // Replace invalid characters with hyphens (preserve alphanumeric, /, ., _, -) + .replace(/[/._-]{2,}/g, (match) => match[0]); // Normalize consecutive special characters to single instances + + // Remove leading/trailing special characters safely without regex backtracking + name = removeLeadingCharacters(name, VALID_TAG_DIRECTORY_SEPARATORS); + name = removeTrailingCharacters(name, VALID_TAG_DIRECTORY_SEPARATORS); + + return name; } /** diff --git a/src/types/config.types.ts b/src/types/config.types.ts index 72086d41..8961f8c9 100644 --- a/src/types/config.types.ts +++ b/src/types/config.types.ts @@ -105,21 +105,38 @@ export interface Config { useSSHSourceFormat: boolean; /** - * The character used to separate the module name from the version in tags. + * The character used to separate directory path components when creating Git tags from module paths. + * This separator is applied throughout the entire directory structure conversion process, not just + * between the module name and version. + * * Must be a single character and one of: -, _, /, . * - * Examples: - * - "/" (default): module/aws-s3-bucket/v1.0.0 - * - "-": module-aws-s3-bucket-v1.0.0 - * - "_": module_aws-s3-bucket_v1.0.0 - * - ".": module.aws-s3-bucket.v1.0.0 + * When converting a module path like 'modules/aws/s3-bucket' to a Git tag, this separator determines + * how directory separators (/) are replaced in the tag name portion: + * + * Examples with module path 'modules/aws/s3-bucket' and version 'v1.0.0': + * - "/" (default): modules/aws/s3-bucket/v1.0.0 + * - "-": modules-aws-s3-bucket-v1.0.0 + * - "_": modules_aws_s3_bucket_v1.0.0 + * - ".": modules.aws.s3-bucket.v1.0.0 + * + * This setting affects tag creation, tag parsing, and tag association logic throughout the system. */ tagDirectorySeparator: string; /** * Whether to include the "v" prefix in version tags. - * When true (default), tags will be formatted as: module/v1.2.3 - * When false, tags will be formatted as: module/1.2.3 + * + * When true (default), version tags will include the "v" prefix: + * - Example: module/v1.2.3 + * + * When false, version tags will not include the "v" prefix: + * - Example: module/1.2.3 + * + * For initial releases, this setting takes precedence over any "v" prefix specified in the + * defaultFirstTag configuration. If useVersionPrefix is false and defaultFirstTag contains + * a "v" prefix (e.g., "v1.0.0"), the "v" will be automatically removed to ensure consistency + * with the useVersionPrefix setting (resulting in "1.0.0"). */ useVersionPrefix: boolean; } diff --git a/src/types/metadata.types.ts b/src/types/metadata.types.ts index 0850844f..4ebfff8e 100644 --- a/src/types/metadata.types.ts +++ b/src/types/metadata.types.ts @@ -1,18 +1,41 @@ import type { Config } from '@/types/config.types'; /** - * Metadata about GitHub Action inputs, including their types, defaults, and mapping to config properties. - * This serves as the single source of truth for action configuration. + * Metadata definition for GitHub Action inputs that enables dynamic configuration mapping. * - * @todo update doc - defaults at runtime come from action.yml, testing see helpers/inputs.ts + * This interface serves as the translation layer between GitHub Action inputs defined in + * action.yml and our internal Config type. It provides the necessary metadata to: + * - Parse input values according to their expected types + * - Map action inputs to the corresponding config property names + * - Enforce required/optional input validation + * - Support dynamic config creation in createConfigFromInputs() + * + * The metadata is used by the ACTION_INPUTS constant in metadata.ts to create a + * comprehensive mapping of all action inputs, which then drives the automatic + * config generation process. + * + * @see {@link /workspaces/terraform-module-releaser/src/utils/metadata.ts} for usage + * @see {@link https://docs.github.com/en/actions/reference/metadata-syntax-for-github-actions#inputs} GitHub Actions input reference */ export interface ActionInputMetadata { - /** The config property name this input maps to */ + /** + * The config property name this input maps to. + * Must be a valid key from the Config interface. + */ configKey: keyof Config; - /** Whether this input is required */ + /** + * Whether this input is required by the GitHub Action. + * When true, the action will fail if the input is not provided. + */ required: boolean; - /** The input type for proper parsing */ + /** + * The expected data type of the input for proper parsing and validation. + * - 'string': Direct string value + * - 'boolean': Parsed using getBooleanInput for proper true/false handling + * - 'number': Parsed using parseInt for integer conversion + * - 'array': Comma-separated string parsed into array with deduplication + */ type: 'string' | 'boolean' | 'number' | 'array'; } diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 630f664d..7ed67192 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -44,8 +44,12 @@ export const VERSION_TAG_REGEX = /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/; * - "module-v1.0.0" → ["module-v1.0.0", "module", "-", "v1.0.0", "1", "0", "0"] * - "feature_2.3.4" → ["feature_2.3.4", "feature", "_", "2.3.4", "2", "3", "4"] * - "service/v0.1.0" → ["service/v0.1.0", "service", "/", "v0.1.0", "0", "1", "0"] + * + * Note: In the character class [-_/.], only the dot (.) requires escaping to match literal periods. + * The hyphen (-) doesn't need escaping when at the start/end of the character class. + * The forward slash (/) doesn't need escaping in JavaScript regex character classes. */ -export const MODULE_TAG_REGEX = /^(.+)([-_\/\.])(v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*))$/; +export const MODULE_TAG_REGEX = /^(.+)([-_/.])(v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*))$/; /** * Release type constants for semantic versioning diff --git a/src/utils/string.ts b/src/utils/string.ts index 95e88172..6a83bb1d 100644 --- a/src/utils/string.ts +++ b/src/utils/string.ts @@ -1,35 +1,3 @@ -/** - * Removes any leading and trailing slashes (/) from the given string. - * - * @param {string} str - The input string from which to trim slashes. - * @returns {string} - The string without leading or trailing slashes. - * - * @example - * // Returns "example/path" - * trimSlashes("/example/path/"); - * - * @example - * // Returns "another/example" - * trimSlashes("///another/example///"); - */ -export function trimSlashes(str: string): string { - let start = 0; - let end = str.length; - - // Remove leading slashes by adjusting start index - while (start < end && str[start] === '/') { - start++; - } - - // Remove trailing slashes by adjusting end index - while (end > start && str[end - 1] === '/') { - end--; - } - - // Return the substring without leading and trailing slashes - return str.slice(start, end); -} - /** * Removes trailing characters from a string without using regex. * @@ -55,5 +23,35 @@ export function removeTrailingCharacters(input: string, charactersToRemove: stri while (endIndex > 0 && charactersToRemove.includes(input[endIndex - 1])) { endIndex--; } + return input.slice(0, endIndex); } + +/** + * Removes leading characters from a string without using regex. + * + * This function iteratively checks each character from the beginning of the string + * and removes any consecutive characters that match the specified characters to remove. + * It uses a direct character-by-character approach instead of regex to avoid potential + * backtracking issues and ensure consistent O(n) performance. + * + * @param {string} input - The string to process + * @param {string[]} charactersToRemove - Array of characters to remove from the beginning + * @returns {string} The input string with all leading specified characters removed + * + * @example + * // Returns "example" + * removeLeadingCharacters("...example", ["."]) + * + * @example + * // Returns "module-name" + * removeLeadingCharacters("._-module-name", [".", "-", "_"]) + */ +export function removeLeadingCharacters(input: string, charactersToRemove: string[]): string { + let startIndex = 0; + while (startIndex < input.length && charactersToRemove.includes(input[startIndex])) { + startIndex++; + } + + return input.slice(startIndex); +} From 99ee82a8625ab278414ae9ec7ad974c6fbff0aa0 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Tue, 19 Aug 2025 00:07:07 +0000 Subject: [PATCH 17/29] fix: remove eslint directive for throwing literals in metadata tests --- __tests__/utils/metadata.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/__tests__/utils/metadata.test.ts b/__tests__/utils/metadata.test.ts index 8220146c..755d8101 100644 --- a/__tests__/utils/metadata.test.ts +++ b/__tests__/utils/metadata.test.ts @@ -15,7 +15,6 @@ describe('utils/metadata', () => { it('should handle non-Error objects thrown during input processing', () => { const errorObject = 'A plain string error'; vi.mocked(getInput).mockImplementation(() => { - // eslint-disable-next-line @typescript-eslint/no-throw-literal throw errorObject; }); From f3e2d30e6025aa016f0923c83d16c5d8cbaede15 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Tue, 19 Aug 2025 00:09:28 +0000 Subject: [PATCH 18/29] chore: update dependencies --- package-lock.json | 760 +++++++++++++++++++++------------------------- 1 file changed, 349 insertions(+), 411 deletions(-) diff --git a/package-lock.json b/package-lock.json index af3108ab..e234be76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -129,13 +129,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", - "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.27.3" + "@babel/types": "^7.28.2" }, "bin": { "parser": "bin/babel-parser.js" @@ -145,9 +145,9 @@ } }, "node_modules/@babel/types": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", - "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "dev": true, "license": "MIT", "dependencies": { @@ -333,9 +333,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", - "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", "cpu": [ "ppc64" ], @@ -350,9 +350,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", - "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", "cpu": [ "arm" ], @@ -367,9 +367,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", - "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", "cpu": [ "arm64" ], @@ -384,9 +384,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", - "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", "cpu": [ "x64" ], @@ -401,9 +401,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", - "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", "cpu": [ "arm64" ], @@ -418,9 +418,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", - "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", "cpu": [ "x64" ], @@ -435,9 +435,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", - "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", "cpu": [ "arm64" ], @@ -452,9 +452,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", - "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", "cpu": [ "x64" ], @@ -469,9 +469,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", - "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", "cpu": [ "arm" ], @@ -486,9 +486,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", - "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", "cpu": [ "arm64" ], @@ -503,9 +503,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", - "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", "cpu": [ "ia32" ], @@ -520,9 +520,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", - "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", "cpu": [ "loong64" ], @@ -537,9 +537,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", - "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", "cpu": [ "mips64el" ], @@ -554,9 +554,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", - "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", "cpu": [ "ppc64" ], @@ -571,9 +571,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", - "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", "cpu": [ "riscv64" ], @@ -588,9 +588,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", - "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", "cpu": [ "s390x" ], @@ -605,9 +605,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", - "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", "cpu": [ "x64" ], @@ -622,9 +622,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", - "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", "cpu": [ "arm64" ], @@ -639,9 +639,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", - "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", "cpu": [ "x64" ], @@ -656,9 +656,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", - "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", "cpu": [ "arm64" ], @@ -673,9 +673,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", - "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", "cpu": [ "x64" ], @@ -689,10 +689,27 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", - "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", "cpu": [ "x64" ], @@ -707,9 +724,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", - "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", "cpu": [ "arm64" ], @@ -724,9 +741,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", - "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", "cpu": [ "ia32" ], @@ -741,9 +758,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", - "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", "cpu": [ "x64" ], @@ -816,18 +833,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -840,27 +853,17 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -869,19 +872,16 @@ } }, "node_modules/@keyv/serialize": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.0.3.tgz", - "integrity": "sha512-qnEovoOp5Np2JDGonIDL6Ayihw0RhnRh6vxPuHo4RDn1UOzwEo4AeIfpL6UGIrsceWrCMiVPgwRjbHu4vYFc3g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.0.tgz", + "integrity": "sha512-RlDgexML7Z63Q8BSaqhXdCYNBy/JQnqYIwxofUrNLGCblOMHp+xux2Q8nLMLlPpgHQPoU0Do8Z6btCpRBEqZ8g==", "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^6.0.3" - } + "license": "MIT" }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.13.0.tgz", - "integrity": "sha512-P5FZsXU0kY881F6Hbk9GhsYx02/KgWK1DYf7/tyE/1lcFKhDYPQR9iYjhQXJn+Sg6hQleMo3DB7h7+p4wgp2Lw==", + "version": "1.17.3", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.3.tgz", + "integrity": "sha512-JPwUKWSsbzx+DLFznf/QZ32Qa+ptfbUlHhRLrBQBAFu9iI1iYvizM4p+zhhRDceSsPutXp4z+R/HPVphlIiclg==", "dev": true, "license": "MIT", "dependencies": { @@ -890,6 +890,7 @@ "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", @@ -911,9 +912,9 @@ } }, "node_modules/@octokit/core": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.2.tgz", - "integrity": "sha512-ODsoD39Lq6vR6aBgvjTnA3nZGliknKboc9Gtxr7E4WDNqY24MxANKcuDQSF0jzapvGb3KWOEDrKfve4HoWGK+g==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.3.tgz", + "integrity": "sha512-oNXsh2ywth5aowwIa7RKtawnkdH6LgU1ztfP9AIUCQCvzysB+WeU8o2kyyosDPwBZutPpjZDKPQGIzzrfTWweQ==", "license": "MIT", "dependencies": { "@octokit/auth-token": "^6.0.0", @@ -962,9 +963,9 @@ "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-13.1.0.tgz", - "integrity": "sha512-16iNOa4rTTjaWtfsPGJcYYL79FJakseX8TQFIPfVuSPC3s5nkS/DSNQPFPc5lJHgEDBWNMxSApHrEymNblhA9w==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-13.1.1.tgz", + "integrity": "sha512-q9iQGlZlxAVNRN2jDNskJW/Cafy7/XE52wjZ5TTvyhyOD904Cvx//DNyoO3J/MXJ0ve3rPoNWKEg5iZrisQSuw==", "license": "MIT", "dependencies": { "@octokit/types": "^14.1.0" @@ -992,9 +993,9 @@ } }, "node_modules/@octokit/request": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.2.tgz", - "integrity": "sha512-iYj4SJG/2bbhh+iIpFmG5u49DtJ4lipQ+aPakjL9OKpsGY93wM8w06gvFbEQxcMsZcCvk5th5KkIm2m8o14aWA==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.3.tgz", + "integrity": "sha512-V6jhKokg35vk098iBqp2FBKunk3kMTXlmq+PtbV9Gl3TfskWlebSofU9uunVKhUN7xl+0+i5vt0TGTG8/p/7HA==", "license": "MIT", "dependencies": { "@octokit/endpoint": "^11.0.0", @@ -1047,9 +1048,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.0.tgz", - "integrity": "sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.3.tgz", + "integrity": "sha512-UmTdvXnLlqQNOCJnyksjPs1G4GqXNGW1LrzCe8+8QoaLhhDeTXYBgJ3k6x61WIhlHX2U+VzEJ55TtIjR/HTySA==", "cpu": [ "arm" ], @@ -1061,9 +1062,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.0.tgz", - "integrity": "sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.3.tgz", + "integrity": "sha512-8NoxqLpXm7VyeI0ocidh335D6OKT0UJ6fHdnIxf3+6oOerZZc+O7r+UhvROji6OspyPm+rrIdb1gTXtVIqn+Sg==", "cpu": [ "arm64" ], @@ -1075,9 +1076,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.0.tgz", - "integrity": "sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.3.tgz", + "integrity": "sha512-csnNavqZVs1+7/hUKtgjMECsNG2cdB8F7XBHP6FfQjqhjF8rzMzb3SLyy/1BG7YSfQ+bG75Ph7DyedbUqwq1rA==", "cpu": [ "arm64" ], @@ -1089,9 +1090,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.0.tgz", - "integrity": "sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.3.tgz", + "integrity": "sha512-r2MXNjbuYabSIX5yQqnT8SGSQ26XQc8fmp6UhlYJd95PZJkQD1u82fWP7HqvGUf33IsOC6qsiV+vcuD4SDP6iw==", "cpu": [ "x64" ], @@ -1103,9 +1104,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.0.tgz", - "integrity": "sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.3.tgz", + "integrity": "sha512-uluObTmgPJDuJh9xqxyr7MV61Imq+0IvVsAlWyvxAaBSNzCcmZlhfYcRhCdMaCsy46ccZa7vtDDripgs9Jkqsw==", "cpu": [ "arm64" ], @@ -1117,9 +1118,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.0.tgz", - "integrity": "sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.3.tgz", + "integrity": "sha512-AVJXEq9RVHQnejdbFvh1eWEoobohUYN3nqJIPI4mNTMpsyYN01VvcAClxflyk2HIxvLpRcRggpX1m9hkXkpC/A==", "cpu": [ "x64" ], @@ -1131,9 +1132,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.0.tgz", - "integrity": "sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.3.tgz", + "integrity": "sha512-byyflM+huiwHlKi7VHLAYTKr67X199+V+mt1iRgJenAI594vcmGGddWlu6eHujmcdl6TqSNnvqaXJqZdnEWRGA==", "cpu": [ "arm" ], @@ -1145,9 +1146,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.0.tgz", - "integrity": "sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.3.tgz", + "integrity": "sha512-aLm3NMIjr4Y9LklrH5cu7yybBqoVCdr4Nvnm8WB7PKCn34fMCGypVNpGK0JQWdPAzR/FnoEoFtlRqZbBBLhVoQ==", "cpu": [ "arm" ], @@ -1159,9 +1160,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.0.tgz", - "integrity": "sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.3.tgz", + "integrity": "sha512-VtilE6eznJRDIoFOzaagQodUksTEfLIsvXymS+UdJiSXrPW7Ai+WG4uapAc3F7Hgs791TwdGh4xyOzbuzIZrnw==", "cpu": [ "arm64" ], @@ -1173,9 +1174,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.0.tgz", - "integrity": "sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.3.tgz", + "integrity": "sha512-dG3JuS6+cRAL0GQ925Vppafi0qwZnkHdPeuZIxIPXqkCLP02l7ka+OCyBoDEv8S+nKHxfjvjW4OZ7hTdHkx8/w==", "cpu": [ "arm64" ], @@ -1187,9 +1188,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.0.tgz", - "integrity": "sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.3.tgz", + "integrity": "sha512-iU8DxnxEKJptf8Vcx4XvAUdpkZfaz0KWfRrnIRrOndL0SvzEte+MTM7nDH4A2Now4FvTZ01yFAgj6TX/mZl8hQ==", "cpu": [ "loong64" ], @@ -1200,10 +1201,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.0.tgz", - "integrity": "sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.3.tgz", + "integrity": "sha512-VrQZp9tkk0yozJoQvQcqlWiqaPnLM6uY1qPYXvukKePb0fqaiQtOdMJSxNFUZFsGw5oA5vvVokjHrx8a9Qsz2A==", "cpu": [ "ppc64" ], @@ -1215,9 +1216,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.0.tgz", - "integrity": "sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.3.tgz", + "integrity": "sha512-uf2eucWSUb+M7b0poZ/08LsbcRgaDYL8NCGjUeFMwCWFwOuFcZ8D9ayPl25P3pl+D2FH45EbHdfyUesQ2Lt9wA==", "cpu": [ "riscv64" ], @@ -1229,9 +1230,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.0.tgz", - "integrity": "sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.3.tgz", + "integrity": "sha512-7tnUcDvN8DHm/9ra+/nF7lLzYHDeODKKKrh6JmZejbh1FnCNZS8zMkZY5J4sEipy2OW1d1Ncc4gNHUd0DLqkSg==", "cpu": [ "riscv64" ], @@ -1243,9 +1244,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.0.tgz", - "integrity": "sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.3.tgz", + "integrity": "sha512-MUpAOallJim8CsJK+4Lc9tQzlfPbHxWDrGXZm2z6biaadNpvh3a5ewcdat478W+tXDoUiHwErX/dOql7ETcLqg==", "cpu": [ "s390x" ], @@ -1257,9 +1258,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.0.tgz", - "integrity": "sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.3.tgz", + "integrity": "sha512-F42IgZI4JicE2vM2PWCe0N5mR5vR0gIdORPqhGQ32/u1S1v3kLtbZ0C/mi9FFk7C5T0PgdeyWEPajPjaUpyoKg==", "cpu": [ "x64" ], @@ -1271,9 +1272,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.0.tgz", - "integrity": "sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.3.tgz", + "integrity": "sha512-oLc+JrwwvbimJUInzx56Q3ujL3Kkhxehg7O1gWAYzm8hImCd5ld1F2Gry5YDjR21MNb5WCKhC9hXgU7rRlyegQ==", "cpu": [ "x64" ], @@ -1285,9 +1286,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.0.tgz", - "integrity": "sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.3.tgz", + "integrity": "sha512-lOrQ+BVRstruD1fkWg9yjmumhowR0oLAAzavB7yFSaGltY8klttmZtCLvOXCmGE9mLIn8IBV/IFrQOWz5xbFPg==", "cpu": [ "arm64" ], @@ -1299,9 +1300,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.0.tgz", - "integrity": "sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.3.tgz", + "integrity": "sha512-vvrVKPRS4GduGR7VMH8EylCBqsDcw6U+/0nPDuIjXQRbHJc6xOBj+frx8ksfZAh6+Fptw5wHrN7etlMmQnPQVg==", "cpu": [ "ia32" ], @@ -1313,9 +1314,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.0.tgz", - "integrity": "sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.3.tgz", + "integrity": "sha512-fi3cPxCnu3ZeM3EwKZPgXbWoGzm2XHgB/WShKI81uj8wG0+laobmqy5wbgEwzstlbLu4MyO8C19FyhhWseYKNQ==", "cpu": [ "x64" ], @@ -1686,9 +1687,9 @@ } }, "node_modules/@types/node": { - "version": "22.15.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.32.tgz", - "integrity": "sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA==", + "version": "22.17.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.2.tgz", + "integrity": "sha512-gL6z5N9Jm9mhY+U2KXZpteb+09zyffliRkZyZOHODGATyC5B1Jt/7TzuuiLkFsSUMLbS1OLmlj/E+/3KF4Q/4w==", "dev": true, "license": "MIT", "dependencies": { @@ -1900,9 +1901,9 @@ } }, "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", "dev": true, "license": "MIT", "engines": { @@ -1946,13 +1947,13 @@ } }, "node_modules/ast-v8-to-istanbul": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.3.tgz", - "integrity": "sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.4.tgz", + "integrity": "sha512-cxrAnZNLBnQwBPByK4CeDaw5sWZtMilJE/Q3iDA0aamgaIVNDF9T6K2/8DfYDZEejZ2jNnDrG9m8MY72HFd0KA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", + "@jridgewell/trace-mapping": "^0.3.29", "estree-walker": "^3.0.3", "js-tokens": "^9.0.1" } @@ -1985,27 +1986,6 @@ "dev": true, "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/before-after-hook": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", @@ -2050,31 +2030,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -2096,14 +2051,14 @@ } }, "node_modules/cacheable": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.10.0.tgz", - "integrity": "sha512-SSgQTAnhd7WlJXnGlIi4jJJOiHzgnM5wRMEPaXAU4kECTAMpBoYKoZ9i5zHmclIEZbxcu3j7yY/CF8DTmwIsHg==", + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.10.4.tgz", + "integrity": "sha512-Gd7ccIUkZ9TE2odLQVS+PDjIvQCdJKUlLdJRVvZu0aipj07Qfx+XIej7hhDrKGGoIxV5m5fT/kOJNJPQhQneRg==", "dev": true, "license": "MIT", "dependencies": { - "hookified": "^1.8.2", - "keyv": "^5.3.3" + "hookified": "^1.11.0", + "keyv": "^5.5.0" } }, "node_modules/call-bind-apply-helpers": { @@ -2149,9 +2104,9 @@ } }, "node_modules/chai": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.1.tgz", + "integrity": "sha512-48af6xm9gQK8rhIcOxWwdGzIervm8BVTin+yRp9HEvU20BtVZ2lBywlIJBzwaDtvo0FvjeL7QdCADoUoqIbV3A==", "dev": true, "license": "MIT", "dependencies": { @@ -2162,7 +2117,7 @@ "pathval": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/chalk": { @@ -2512,9 +2467,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", - "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2525,31 +2480,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.5", - "@esbuild/android-arm": "0.25.5", - "@esbuild/android-arm64": "0.25.5", - "@esbuild/android-x64": "0.25.5", - "@esbuild/darwin-arm64": "0.25.5", - "@esbuild/darwin-x64": "0.25.5", - "@esbuild/freebsd-arm64": "0.25.5", - "@esbuild/freebsd-x64": "0.25.5", - "@esbuild/linux-arm": "0.25.5", - "@esbuild/linux-arm64": "0.25.5", - "@esbuild/linux-ia32": "0.25.5", - "@esbuild/linux-loong64": "0.25.5", - "@esbuild/linux-mips64el": "0.25.5", - "@esbuild/linux-ppc64": "0.25.5", - "@esbuild/linux-riscv64": "0.25.5", - "@esbuild/linux-s390x": "0.25.5", - "@esbuild/linux-x64": "0.25.5", - "@esbuild/netbsd-arm64": "0.25.5", - "@esbuild/netbsd-x64": "0.25.5", - "@esbuild/openbsd-arm64": "0.25.5", - "@esbuild/openbsd-x64": "0.25.5", - "@esbuild/sunos-x64": "0.25.5", - "@esbuild/win32-arm64": "0.25.5", - "@esbuild/win32-ia32": "0.25.5", - "@esbuild/win32-x64": "0.25.5" + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" } }, "node_modules/escape-html": { @@ -2620,19 +2576,19 @@ } }, "node_modules/eventsource-parser": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.2.tgz", - "integrity": "sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.5.tgz", + "integrity": "sha512-bSRG85ZrMdmWtm7qkF9He9TNRzc/Bm99gEJMaQoHJ9E6Kv9QBbsldh2oMj7iXmYNEAVvNgvv5vPorG6W+XtBhQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/expect-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", - "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2683,9 +2639,9 @@ } }, "node_modules/express-rate-limit": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", - "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", "dev": true, "license": "MIT", "engines": { @@ -2695,7 +2651,7 @@ "url": "https://github.com/sponsors/express-rate-limit" }, "peerDependencies": { - "express": "^4.11 || 5 || ^5.0.0-beta.1" + "express": ">= 4.11" } }, "node_modules/extend": { @@ -2781,11 +2737,14 @@ } }, "node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -2796,13 +2755,13 @@ } }, "node_modules/file-entry-cache": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.1.1.tgz", - "integrity": "sha512-zcmsHjg2B2zjuBgjdnB+9q0+cWcgWfykIcsDkWDB4GTPtl1eXUA+gTI6sO0u01AqK3cliHryTU55/b2Ow1hfZg==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.1.4.tgz", + "integrity": "sha512-5XRUFc0WTtUbjfGzEwXc42tiGxQHBmtbUG1h9L2apu4SulCGN3Hqm//9D6FAolf8MYNL7f/YlJl9vy08pj5JuA==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^6.1.10" + "flat-cache": "^6.1.13" } }, "node_modules/finalhandler": { @@ -2837,15 +2796,15 @@ } }, "node_modules/flat-cache": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.10.tgz", - "integrity": "sha512-B6/v1f0NwjxzmeOhzfXPGWpKBVA207LS7lehaVKQnFrVktcFRfkzjZZ2gwj2i1TkEUMQht7ZMJbABUT5N+V1Nw==", + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.13.tgz", + "integrity": "sha512-gmtS2PaUjSPa4zjObEIn4WWliKyZzYljgxODBfxugpK6q6HU9ClXzgCJ+nlcPKY9Bt090ypTOLIFWkV0jbKFjw==", "dev": true, "license": "MIT", "dependencies": { - "cacheable": "^1.10.0", + "cacheable": "^1.10.4", "flatted": "^3.3.3", - "hookified": "^1.9.1" + "hookified": "^1.11.0" } }, "node_modules/flatted": { @@ -3072,9 +3031,9 @@ } }, "node_modules/hookified": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.9.1.tgz", - "integrity": "sha512-u3pxtGhKjcSXnGm1CX6aXS9xew535j3lkOCegbA6jdyh0BaAjTbXI4aslKstCr6zUNtoCxFGFKwjbSHdGrMB8g==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.11.0.tgz", + "integrity": "sha512-aDdIN3GyU5I6wextPplYdfmWCo+aLmjjVbntmX6HLD5RCi/xKsivYEBhnRD+d9224zFf008ZpLMPlWF0ZodYZw==", "dev": true, "license": "MIT" }, @@ -3132,27 +3091,6 @@ "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -3332,9 +3270,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -3409,13 +3347,13 @@ } }, "node_modules/keyv": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.3.4.tgz", - "integrity": "sha512-ypEvQvInNpUe+u+w8BIcPkQvEqXquyyibWE/1NB5T2BTzIpS5cGEV1LZskDzPSTvNAaT4+5FutvzlvnkxOSKlw==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.0.tgz", + "integrity": "sha512-QG7qR2tijh1ftOvClut4YKKg1iW6cx3GZsKoGyJPxHkGWK9oJhG9P3j5deP0QQOGDowBMVQFaP+Vm4NpGYvmIQ==", "dev": true, "license": "MIT", "dependencies": { - "@keyv/serialize": "^1.0.3" + "@keyv/serialize": "^1.1.0" } }, "node_modules/levn": { @@ -3489,9 +3427,9 @@ } }, "node_modules/loupe": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", - "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz", + "integrity": "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==", "dev": true, "license": "MIT" }, @@ -4118,9 +4056,9 @@ } }, "node_modules/openai": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/openai/-/openai-5.5.1.tgz", - "integrity": "sha512-5i19097mGotHA1eFsM6Tjd/tJ8uo9sa5Ysv4Q6bKJ2vtN6rc0MzMrUefXnLXYAJcmMQrC1Efhj0AvfIkXrQamw==", + "version": "5.12.2", + "resolved": "https://registry.npmjs.org/openai/-/openai-5.12.2.tgz", + "integrity": "sha512-xqzHHQch5Tws5PcKR2xsZGX9xtch+JQFz5zb14dGqlshmmDAFBFEWmeIpf7wVqWV+w7Emj7jRgkNJakyKE0tYQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4341,9 +4279,9 @@ "license": "MIT" }, "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", "dev": true, "license": "MIT", "engines": { @@ -4358,9 +4296,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -4756,9 +4694,9 @@ } }, "node_modules/rollup": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.0.tgz", - "integrity": "sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.3.tgz", + "integrity": "sha512-RZn2XTjXb8t5g13f5YclGoilU/kwT696DIkY3sywjdZidNSi3+vseaQov7D7BZXVJCPv3pDWUN69C78GGbXsKw==", "dev": true, "license": "MIT", "dependencies": { @@ -4772,26 +4710,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.44.0", - "@rollup/rollup-android-arm64": "4.44.0", - "@rollup/rollup-darwin-arm64": "4.44.0", - "@rollup/rollup-darwin-x64": "4.44.0", - "@rollup/rollup-freebsd-arm64": "4.44.0", - "@rollup/rollup-freebsd-x64": "4.44.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.44.0", - "@rollup/rollup-linux-arm-musleabihf": "4.44.0", - "@rollup/rollup-linux-arm64-gnu": "4.44.0", - "@rollup/rollup-linux-arm64-musl": "4.44.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.44.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.44.0", - "@rollup/rollup-linux-riscv64-gnu": "4.44.0", - "@rollup/rollup-linux-riscv64-musl": "4.44.0", - "@rollup/rollup-linux-s390x-gnu": "4.44.0", - "@rollup/rollup-linux-x64-gnu": "4.44.0", - "@rollup/rollup-linux-x64-musl": "4.44.0", - "@rollup/rollup-win32-arm64-msvc": "4.44.0", - "@rollup/rollup-win32-ia32-msvc": "4.44.0", - "@rollup/rollup-win32-x64-msvc": "4.44.0", + "@rollup/rollup-android-arm-eabi": "4.46.3", + "@rollup/rollup-android-arm64": "4.46.3", + "@rollup/rollup-darwin-arm64": "4.46.3", + "@rollup/rollup-darwin-x64": "4.46.3", + "@rollup/rollup-freebsd-arm64": "4.46.3", + "@rollup/rollup-freebsd-x64": "4.46.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.3", + "@rollup/rollup-linux-arm-musleabihf": "4.46.3", + "@rollup/rollup-linux-arm64-gnu": "4.46.3", + "@rollup/rollup-linux-arm64-musl": "4.46.3", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.3", + "@rollup/rollup-linux-ppc64-gnu": "4.46.3", + "@rollup/rollup-linux-riscv64-gnu": "4.46.3", + "@rollup/rollup-linux-riscv64-musl": "4.46.3", + "@rollup/rollup-linux-s390x-gnu": "4.46.3", + "@rollup/rollup-linux-x64-gnu": "4.46.3", + "@rollup/rollup-linux-x64-musl": "4.46.3", + "@rollup/rollup-win32-arm64-msvc": "4.46.3", + "@rollup/rollup-win32-ia32-msvc": "4.46.3", + "@rollup/rollup-win32-x64-msvc": "4.46.3", "fsevents": "~2.3.2" } }, @@ -5076,9 +5014,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.21", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", - "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", "dev": true, "license": "CC0-1.0" }, @@ -5231,9 +5169,9 @@ } }, "node_modules/strip-json-comments": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.2.tgz", - "integrity": "sha512-4X2FR3UwhNUE9G49aIsJW5hRRR3GXGTBTZRMfv568O60ojM8HcWjV/VxAxCDW3SUND33O6ZY66ZuRcdkj73q2g==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", "dev": true, "license": "MIT", "engines": { @@ -5469,28 +5407,28 @@ } }, "node_modules/textlint-rule-helper": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/textlint-rule-helper/-/textlint-rule-helper-2.3.1.tgz", - "integrity": "sha512-b1bijvyiUmKinfFE5hkQMSXs3Ky8jyZ3Y6SOoTRJKV9HLL2LWUVFAUezO7z4FpAkVvYruDYWCwA5qWV8GmvyUw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/textlint-rule-helper/-/textlint-rule-helper-2.5.0.tgz", + "integrity": "sha512-QIbFPtyqLy0g5BJn8mryk9iHzGYicNaFIpLFPiEnb4RXxrEGeQ2W2aARQ9yEXLIAqo+OwK4ndWBAWkbgJEPzTQ==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "^13.4.1", + "@textlint/ast-node-types": "^15.2.1", "structured-source": "^4.0.0", "unist-util-visit": "^2.0.3" } }, "node_modules/textlint-rule-helper/node_modules/@textlint/ast-node-types": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-13.4.1.tgz", - "integrity": "sha512-qrZyhCh8Ekk6nwArx3BROybm9BnX6vF7VcZbijetV/OM3yfS4rTYhoMWISmhVEP2H2re0CtWEyMl/XF+WdvVLQ==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.2.1.tgz", + "integrity": "sha512-20fEcLPsXg81yWpApv4FQxrZmlFF/Ta7/kz1HGIL+pJo5cSTmkc+eCki3GpOPZIoZk0tbJU8hrlwUb91F+3SNQ==", "dev": true, "license": "MIT" }, "node_modules/textlint-rule-terminology": { - "version": "5.2.12", - "resolved": "https://registry.npmjs.org/textlint-rule-terminology/-/textlint-rule-terminology-5.2.12.tgz", - "integrity": "sha512-qLyuqbGN7GBqKR7NT2yLyo8mmYmoCkk8bu3szvLxUkM+1Cc0D9G6MDyaYFrMM26GZuNbTREmuFxon/vTBDR/pw==", + "version": "5.2.14", + "resolved": "https://registry.npmjs.org/textlint-rule-terminology/-/textlint-rule-terminology-5.2.14.tgz", + "integrity": "sha512-OYvLq+K+HpQqxWhV9MXb/jnND8oWxUbZK+OmrqO8auO2TWMky22ZlR8qoAmlzYbRMR1S3LJyifFlThZoXbD1uA==", "dev": true, "license": "MIT", "dependencies": { @@ -5595,9 +5533,9 @@ } }, "node_modules/tsx": { - "version": "4.20.3", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", - "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", + "version": "4.20.4", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.4.tgz", + "integrity": "sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg==", "dev": true, "license": "MIT", "dependencies": { @@ -5652,9 +5590,9 @@ } }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5894,24 +5832,24 @@ } }, "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.2.tgz", + "integrity": "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" + "fdir": "^6.4.6", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.14" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -5920,14 +5858,14 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", - "less": "*", + "less": "^4.0.0", "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" @@ -6221,9 +6159,9 @@ } }, "node_modules/zod": { - "version": "3.25.67", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", - "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "dev": true, "license": "MIT", "funding": { @@ -6231,9 +6169,9 @@ } }, "node_modules/zod-to-json-schema": { - "version": "3.24.5", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", - "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", "dev": true, "license": "ISC", "peerDependencies": { From f2e18747eb75a31204e8f5e33012e4d5ab3b81a8 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Tue, 19 Aug 2025 00:19:40 +0000 Subject: [PATCH 19/29] chore: update dependencies and improve variable naming - Updated `p-limit` from version 6.2.0 to 7.0.0 in package.json. - Updated `@biomejs/biome` from version 1.9.4 to 2.2.0 in devDependencies. - Updated `textlint` from version 14.8.0 to 15.2.1 in devDependencies. - Refactored variable names in `dev-parse-modules.ts` for clarity: changed `config` to `_config`, `context` to `_context`, and `modules` to `_modules`. - Improved backward compatibility proxy in `config.ts` and `context.ts` by renaming the target parameter to `_target` for better readability. --- __mocks__/@actions/core.ts | 32 +- __mocks__/context.ts | 6 +- __tests__/context.test.ts | 2 +- __tests__/tags.test.ts | 4 +- __tests__/terraform-docs.test.ts | 2 +- __tests__/utils/file.test.ts | 4 +- __tests__/wiki.test.ts | 6 +- biome.json | 10 +- package-lock.json | 786 +++++++++++-------------------- package.json | 6 +- scripts/dev-parse-modules.ts | 6 +- src/config.ts | 2 +- src/context.ts | 2 +- 13 files changed, 314 insertions(+), 554 deletions(-) diff --git a/__mocks__/@actions/core.ts b/__mocks__/@actions/core.ts index e47f4769..9bb718e2 100644 --- a/__mocks__/@actions/core.ts +++ b/__mocks__/@actions/core.ts @@ -14,13 +14,13 @@ type MockedFunction = TFunc extends (...args: infer TArgs) => infer TRetu * Writes a debug message to the user log. * @param message - The debug message to log. */ -export const debug: MockedFunction<(message: string) => void> = vi.fn((message: string): void => {}); +export const debug: MockedFunction<(message: string) => void> = vi.fn((_message: string): void => {}); /** * Writes an informational message to the log. * @param message - The info message to log. */ -export const info: MockedFunction<(message: string) => void> = vi.fn((message: string): void => {}); +export const info: MockedFunction<(message: string) => void> = vi.fn((_message: string): void => {}); /** * Adds a warning issue with optional annotation properties. @@ -28,7 +28,7 @@ export const info: MockedFunction<(message: string) => void> = vi.fn((message: s * @param properties - Optional properties to add to the annotation. */ export const warning: MockedFunction<(message: string | Error, properties?: AnnotationProperties) => void> = vi.fn( - (message: string | Error, properties?: AnnotationProperties): void => {}, + (_message: string | Error, _properties?: AnnotationProperties): void => {}, ); /** @@ -37,7 +37,7 @@ export const warning: MockedFunction<(message: string | Error, properties?: Anno * @param properties - Optional properties to add to the annotation. */ export const notice: MockedFunction<(message: string | Error, properties?: AnnotationProperties) => void> = vi.fn( - (message: string | Error, properties?: AnnotationProperties): void => {}, + (_message: string | Error, _properties?: AnnotationProperties): void => {}, ); /** @@ -46,7 +46,7 @@ export const notice: MockedFunction<(message: string | Error, properties?: Annot * @param properties - Optional properties to add to the annotation. */ export const error: MockedFunction<(message: string | Error, properties?: AnnotationProperties) => void> = vi.fn( - (message: string | Error, properties?: AnnotationProperties): void => {}, + (_message: string | Error, _properties?: AnnotationProperties): void => {}, ); /** @@ -55,13 +55,13 @@ export const error: MockedFunction<(message: string | Error, properties?: Annota * @param message - The error message or object. * @throws An error with the specified message. */ -export const setFailed: MockedFunction<(message: string | Error) => void> = vi.fn((message: string | Error) => {}); +export const setFailed: MockedFunction<(message: string | Error) => void> = vi.fn((_message: string | Error) => {}); /** * Begins a new output group. Output until the next `endGroup` will be foldable in this group. * @param name - The name of the output group. */ -export const startGroup: MockedFunction<(name: string) => void> = vi.fn((name: string): void => {}); +export const startGroup: MockedFunction<(name: string) => void> = vi.fn((_name: string): void => {}); /** * Ends the current output group. @@ -109,13 +109,13 @@ export const getMultilineInput: MockedFunction<(name: string, options?: InputOpt * Masks a value in the log. When the masked value appears in the log, it is replaced with asterisks. * @param secret - Value to mask */ -export const setSecret: MockedFunction<(secret: string) => void> = vi.fn((secret: string): void => {}); +export const setSecret: MockedFunction<(secret: string) => void> = vi.fn((_secret: string): void => {}); /** * Prepends the given path to the PATH environment variable. * @param inputPath - Path to prepend */ -export const addPath: MockedFunction<(inputPath: string) => void> = vi.fn((inputPath: string): void => {}); +export const addPath: MockedFunction<(inputPath: string) => void> = vi.fn((_inputPath: string): void => {}); /** * Sets env variable for this action and future actions in the job. @@ -123,14 +123,14 @@ export const addPath: MockedFunction<(inputPath: string) => void> = vi.fn((input * @param val - Value of the variable */ export const exportVariable: MockedFunction<(name: string, val: string) => void> = vi.fn( - (name: string, val: string): void => {}, + (_name: string, _val: string): void => {}, ); /** * Enables or disables the echoing of commands into stdout for the rest of the step. * @param enabled - True to enable echoing, false to disable */ -export const setCommandEcho: MockedFunction<(enabled: boolean) => void> = vi.fn((enabled: boolean): void => {}); +export const setCommandEcho: MockedFunction<(enabled: boolean) => void> = vi.fn((_enabled: boolean): void => {}); /** * Begin an output group. @@ -138,7 +138,7 @@ export const setCommandEcho: MockedFunction<(enabled: boolean) => void> = vi.fn( * @param fn - Function to execute within the output group */ export const group: MockedFunction<(name: string, fn: () => Promise) => Promise> = vi.fn( - async (name: string, fn: () => Promise): Promise => { + async (_name: string, fn: () => Promise): Promise => { await fn(); }, ); @@ -150,7 +150,7 @@ export const group: MockedFunction<(name: string, fn: () => Promise) => Pr * @param value - Value to store. Non-string values will be converted to a string via JSON.stringify */ export const saveState: MockedFunction<(name: string, value: string) => void> = vi.fn( - (name: string, value: string): void => {}, + (_name: string, _value: string): void => {}, ); /** @@ -158,7 +158,7 @@ export const saveState: MockedFunction<(name: string, value: string) => void> = * @param name - Name of the state to get * @returns string */ -export const getState: MockedFunction<(name: string) => string> = vi.fn((name: string): string => ''); +export const getState: MockedFunction<(name: string) => string> = vi.fn((_name: string): string => ''); /** * Gets whether Actions Step Debug is on or not @@ -172,7 +172,7 @@ export const isDebug: MockedFunction<() => boolean> = vi.fn((): boolean => false * @returns string */ export const getIDToken: MockedFunction<(audience?: string) => Promise> = vi.fn( - async (audience?: string): Promise => '', + async (_audience?: string): Promise => '', ); /** @@ -181,7 +181,7 @@ export const getIDToken: MockedFunction<(audience?: string) => Promise> * @param value - Value to store. Non-string values will be converted to a string via JSON.stringify */ export const setOutput: MockedFunction<(name: string, value: string) => void> = vi.fn( - (name: string, value: string): void => {}, + (_name: string, _value: string): void => {}, ); // Re-export types diff --git a/__mocks__/context.ts b/__mocks__/context.ts index 11ad91d0..5c0a9595 100644 --- a/__mocks__/context.ts +++ b/__mocks__/context.ts @@ -59,7 +59,7 @@ let currentContext: Context = { ...defaultContext }; * Context proxy handler */ const contextProxyHandler: ProxyHandler = { - set(target: ContextWithMethods, key: string, value: unknown): boolean { + set(_target: ContextWithMethods, key: string, value: unknown): boolean { if (!validContextKeys.includes(key as ValidContextKey)) { throw new Error(`Invalid context key: ${key}`); } @@ -68,7 +68,7 @@ const contextProxyHandler: ProxyHandler = { const expectedValue = defaultContext[typedKey]; if (typeof expectedValue === typeof value || (typedKey === 'octokit' && typeof value === 'object')) { - // @ts-ignore - we know the key is valid and value type is correct + // @ts-expect-error - we know the key is valid and value type is correct currentContext[typedKey] = value; return true; } @@ -76,7 +76,7 @@ const contextProxyHandler: ProxyHandler = { throw new TypeError(`Invalid value type for context key: ${key}`); }, - get(target: ContextWithMethods, prop: string | symbol): unknown { + get(_target: ContextWithMethods, prop: string | symbol): unknown { if (typeof prop === 'string') { if (prop === 'set') { return (overrides: Partial = {}) => { diff --git a/__tests__/context.test.ts b/__tests__/context.test.ts index b84b20cf..923a4830 100644 --- a/__tests__/context.test.ts +++ b/__tests__/context.test.ts @@ -216,7 +216,7 @@ describe('context', () => { vi.mocked(startGroup).mockClear(); // Second access should not trigger initialization - const prNumber = context.prNumber; // Intentionally access a property with no usage + const _prNumber = context.prNumber; // Intentionally access a property with no usage expect(startGroup).not.toHaveBeenCalled(); expect(info).not.toHaveBeenCalled(); }); diff --git a/__tests__/tags.test.ts b/__tests__/tags.test.ts index 44a05ae2..51836ddf 100644 --- a/__tests__/tags.test.ts +++ b/__tests__/tags.test.ts @@ -53,7 +53,7 @@ describe('tags', () => { expect(debugCall).toBeDefined(); // Ensure there is a debug call const debugMessage = debugCall[0]; expect(/^Total page requests: \d+$/.test(debugMessage)).toBe(true); // Check if it matches the format - expect(Number.parseInt(debugMessage.split(': ')[1])).toBeGreaterThan(1); // Check if number > 1 + expect(Number.parseInt(debugMessage.split(': ')[1], 10)).toBeGreaterThan(1); // Check if number > 1 // Check the first info call for "Found X tags" const infoCall = vi.mocked(info).mock.calls[0]; // Get the first call @@ -270,7 +270,7 @@ describe('tags', () => { await expect(deleteTags(tagsToDelete)).rejects.toThrow( `Failed to delete repository tag: v1.0.0 Resource not accessible by integration. -Ensure that the GitHub Actions workflow has the correct permissions to delete tags by ensuring that your workflow YAML file has the following block under \"permissions\": +Ensure that the GitHub Actions workflow has the correct permissions to delete tags by ensuring that your workflow YAML file has the following block under "permissions": permissions: contents: write`, diff --git a/__tests__/terraform-docs.test.ts b/__tests__/terraform-docs.test.ts index df851ef4..214114d6 100644 --- a/__tests__/terraform-docs.test.ts +++ b/__tests__/terraform-docs.test.ts @@ -251,7 +251,7 @@ describe('terraform-docs', async () => { for (const file of cleanupFiles) { try { unlinkSync(file); - } catch (err) { + } catch (_err) { // Ignore cleanup errors } } diff --git a/__tests__/utils/file.test.ts b/__tests__/utils/file.test.ts index 558f46ff..989a42a2 100644 --- a/__tests__/utils/file.test.ts +++ b/__tests__/utils/file.test.ts @@ -619,7 +619,7 @@ describe('utils/file', () => { // Create terraform file above workspace (if possible) try { writeFileSync(terraformFileAbove, 'resource "test" "example" {}'); - } catch (error) { + } catch (_error) { // Skip this test if we can't write above tmpDir return; } @@ -635,7 +635,7 @@ describe('utils/file', () => { // Cleanup try { rmSync(terraformFileAbove); - } catch (error) { + } catch (_error) { // Ignore cleanup errors } }); diff --git a/__tests__/wiki.test.ts b/__tests__/wiki.test.ts index 2b003ba4..0c587380 100644 --- a/__tests__/wiki.test.ts +++ b/__tests__/wiki.test.ts @@ -103,7 +103,7 @@ describe('wiki', async () => { it('should handle unsetting config extraheader and throwing error accordingly', () => { const mockExecFileSync = vi.fn( - (command: string, args?: readonly string[] | undefined, options?: ExecFileSyncOptions) => { + (_command: string, args?: readonly string[] | undefined, _options?: ExecFileSyncOptions) => { if (args?.includes('--unset-all') && args.includes('http.https://github.com/.extraheader')) { const error = new Error('git config error') as ExecSyncError; error.status = 10; @@ -131,7 +131,7 @@ describe('wiki', async () => { it('should handle unsetting config extraheader gracefully', () => { const mockExecFileSync = vi.fn( - (command: string, args?: readonly string[] | undefined, options?: ExecFileSyncOptions) => { + (_command: string, args?: readonly string[] | undefined, _options?: ExecFileSyncOptions) => { if (args?.includes('--unset-all') && args.includes('http.https://github.com/.extraheader')) { const error = new Error('git config error') as ExecSyncError; error.status = 5; @@ -168,7 +168,7 @@ describe('wiki', async () => { // Reset mocks and configure remote command to return "origin" vi.clearAllMocks(); - vi.mocked(execFileSync).mockImplementation((cmd, args = []) => { + vi.mocked(execFileSync).mockImplementation((_cmd, args = []) => { if (args[0] === 'remote') { return Buffer.from('origin'); } diff --git a/biome.json b/biome.json index 3d7ea7c8..7ed801a8 100644 --- a/biome.json +++ b/biome.json @@ -1,12 +1,15 @@ { - "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", + "$schema": "https://biomejs.dev/schemas/2.2.0/schema.json", "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true }, + "assist": { + "enabled": false + }, "files": { - "ignore": ["dist", "node_modules"] + "includes": ["*.md", "*.ts", "__mocks__/**/*.ts", "__tests__/**/*.ts", "src/**/*.ts", "scripts/**/*.ts"] }, "formatter": { "enabled": true, @@ -24,9 +27,6 @@ } } }, - "organizeImports": { - "enabled": true - }, "javascript": { "formatter": { "enabled": true, diff --git a/package-lock.json b/package-lock.json index e234be76..d6a3a70b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,11 +15,11 @@ "@octokit/plugin-rest-endpoint-methods": "^16.0.0", "@octokit/request-error": "^7.0.0", "minimatch": "^10.0.1", - "p-limit": "^6.2.0", + "p-limit": "^7.0.0", "which": "^5.0.0" }, "devDependencies": { - "@biomejs/biome": "^1.9.4", + "@biomejs/biome": "^2.2.0", "@octokit/types": "^14.0.0", "@octokit/webhooks-types": "^7.6.1", "@types/js-yaml": "^4.0.9", @@ -30,7 +30,7 @@ "js-yaml": "^4.1.0", "make-coverage-badge": "^1.2.0", "openai": "latest", - "textlint": "^14.8.0", + "textlint": "^15.2.1", "textlint-filter-rule-comments": "^1.2.2", "textlint-rule-terminology": "^5.2.12", "ts-deepmerge": "^7.0.2", @@ -108,6 +108,28 @@ "@azu/format-text": "^1.0.1" } }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -169,11 +191,10 @@ } }, "node_modules/@biomejs/biome": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz", - "integrity": "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.0.tgz", + "integrity": "sha512-3On3RSYLsX+n9KnoSgfoYlckYBoU6VRM22cw1gB4Y0OuUVSYd/O/2saOJMrA4HFfA1Ff0eacOvMN1yAAvHtzIw==", "dev": true, - "hasInstallScript": true, "license": "MIT OR Apache-2.0", "bin": { "biome": "bin/biome" @@ -186,20 +207,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "1.9.4", - "@biomejs/cli-darwin-x64": "1.9.4", - "@biomejs/cli-linux-arm64": "1.9.4", - "@biomejs/cli-linux-arm64-musl": "1.9.4", - "@biomejs/cli-linux-x64": "1.9.4", - "@biomejs/cli-linux-x64-musl": "1.9.4", - "@biomejs/cli-win32-arm64": "1.9.4", - "@biomejs/cli-win32-x64": "1.9.4" + "@biomejs/cli-darwin-arm64": "2.2.0", + "@biomejs/cli-darwin-x64": "2.2.0", + "@biomejs/cli-linux-arm64": "2.2.0", + "@biomejs/cli-linux-arm64-musl": "2.2.0", + "@biomejs/cli-linux-x64": "2.2.0", + "@biomejs/cli-linux-x64-musl": "2.2.0", + "@biomejs/cli-win32-arm64": "2.2.0", + "@biomejs/cli-win32-x64": "2.2.0" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz", - "integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.2.0.tgz", + "integrity": "sha512-zKbwUUh+9uFmWfS8IFxmVD6XwqFcENjZvEyfOxHs1epjdH3wyyMQG80FGDsmauPwS2r5kXdEM0v/+dTIA9FXAg==", "cpu": [ "arm64" ], @@ -214,9 +235,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz", - "integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.2.0.tgz", + "integrity": "sha512-+OmT4dsX2eTfhD5crUOPw3RPhaR+SKVspvGVmSdZ9y9O/AgL8pla6T4hOn1q+VAFBHuHhsdxDRJgFCSC7RaMOw==", "cpu": [ "x64" ], @@ -231,9 +252,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz", - "integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.2.0.tgz", + "integrity": "sha512-6eoRdF2yW5FnW9Lpeivh7Mayhq0KDdaDMYOJnH9aT02KuSIX5V1HmWJCQQPwIQbhDh68Zrcpl8inRlTEan0SXw==", "cpu": [ "arm64" ], @@ -248,9 +269,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz", - "integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.0.tgz", + "integrity": "sha512-egKpOa+4FL9YO+SMUMLUvf543cprjevNc3CAgDNFLcjknuNMcZ0GLJYa3EGTCR2xIkIUJDVneBV3O9OcIlCEZQ==", "cpu": [ "arm64" ], @@ -265,9 +286,9 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz", - "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.2.0.tgz", + "integrity": "sha512-5UmQx/OZAfJfi25zAnAGHUMuOd+LOsliIt119x2soA2gLggQYrVPA+2kMUxR6Mw5M1deUF/AWWP2qpxgH7Nyfw==", "cpu": [ "x64" ], @@ -282,9 +303,9 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz", - "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.0.tgz", + "integrity": "sha512-I5J85yWwUWpgJyC1CcytNSGusu2p9HjDnOPAFG4Y515hwRD0jpR9sT9/T1cKHtuCvEQ/sBvx+6zhz9l9wEJGAg==", "cpu": [ "x64" ], @@ -299,9 +320,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz", - "integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.2.0.tgz", + "integrity": "sha512-n9a1/f2CwIDmNMNkFs+JI0ZjFnMO0jdOyGNtihgUNFnlmd84yIYY2KMTBmMV58ZlVHjgmY5Y6E1hVTnSRieggA==", "cpu": [ "arm64" ], @@ -316,9 +337,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz", - "integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.2.0.tgz", + "integrity": "sha512-Nawu5nHjP/zPKTIryh2AavzTc/KEg4um/MxWdXW0A6P/RZOyIpa7+QSjeXwAwX/utJGaCoXRPWtF3m5U/bB3Ww==", "cpu": [ "x64" ], @@ -1328,66 +1349,66 @@ ] }, "node_modules/@textlint/ast-node-types": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-14.8.4.tgz", - "integrity": "sha512-+fI7miec/r9VeniFV9ppL4jRCmHNsTxieulTUf/4tvGII3db5hGriKHC4p/diq1SkQ9Sgs7kg6UyydxZtpTz1Q==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.2.1.tgz", + "integrity": "sha512-20fEcLPsXg81yWpApv4FQxrZmlFF/Ta7/kz1HGIL+pJo5cSTmkc+eCki3GpOPZIoZk0tbJU8hrlwUb91F+3SNQ==", "dev": true, "license": "MIT" }, "node_modules/@textlint/ast-tester": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/@textlint/ast-tester/-/ast-tester-14.8.4.tgz", - "integrity": "sha512-j6YKPuEaASeXQ2Y/ode993r4A8ugdGEFnPhp96HVGjNVoAsandlR/L0WEMDG1FdIJj3W9+9rlcikXhFQSFc0lA==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/ast-tester/-/ast-tester-15.2.1.tgz", + "integrity": "sha512-B3BNE52w+6eCsybpKhxIm/bMY1i3oF8AC5amYeaPaTaluz+rPDR4S5S6QAMaMM8XJlD0osYBdKd9LDwQPJFsIQ==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "14.8.4", + "@textlint/ast-node-types": "15.2.1", "debug": "^4.4.1" } }, "node_modules/@textlint/ast-traverse": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/@textlint/ast-traverse/-/ast-traverse-14.8.4.tgz", - "integrity": "sha512-bnmgt0dB5RxBhRXQnaTd6wblfuv+cRWrGuyMp6CIuPTyWXyA5AO3NhqQYjQLCbrPDByiwbHAQwIZYOw6sVvn9Q==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/ast-traverse/-/ast-traverse-15.2.1.tgz", + "integrity": "sha512-6i+kqjREEJ1HH3v0tltQ1ZTgptd4ViyJiZe+5J62Bn1Ml7CyV/zIJ4+3pJ4x26Ts+1sqpUD/lDDNOeZz5DKsmg==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "14.8.4" + "@textlint/ast-node-types": "15.2.1" } }, "node_modules/@textlint/config-loader": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/@textlint/config-loader/-/config-loader-14.8.4.tgz", - "integrity": "sha512-TWIfYkGIl6zZz4GJWQVrWurK25YG0j0Br/Jexn2EAh7sun5wDsb7hHK1Y2aWHIAeWHOn5D2C0OdHT3jH8YToGA==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/config-loader/-/config-loader-15.2.1.tgz", + "integrity": "sha512-Rxp9YUAyYDmy6VIFJ0aNE/18O920wGCJpAVok+TnW8VM/CJXTp6/UibgSkqjWM5W2K301AFhZgPENAWJQLP7yw==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/kernel": "14.8.4", - "@textlint/module-interop": "14.8.4", - "@textlint/resolver": "14.8.4", - "@textlint/types": "14.8.4", - "@textlint/utils": "14.8.4", + "@textlint/kernel": "15.2.1", + "@textlint/module-interop": "15.2.1", + "@textlint/resolver": "15.2.1", + "@textlint/types": "15.2.1", + "@textlint/utils": "15.2.1", "debug": "^4.4.1", "rc-config-loader": "^4.1.3" } }, "node_modules/@textlint/feature-flag": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/@textlint/feature-flag/-/feature-flag-14.8.4.tgz", - "integrity": "sha512-bI1HpZtArzgmbPsMubKe3AYLIOYPOqHJ8R8JlhSuduszVd6gFsyptmMTHdI+1gWRTo1Dv9LRGEmI9W9rAV7Dmg==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/feature-flag/-/feature-flag-15.2.1.tgz", + "integrity": "sha512-88H5WGxTperDNHA6LXT6AcTJ9MFs9l1OR7fjq+0AbXjq8Fg5RFYgx0SCxr4Fcmx0nr8JOFhexXp8qz6MMUz+bg==", "dev": true, "license": "MIT" }, "node_modules/@textlint/fixer-formatter": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/@textlint/fixer-formatter/-/fixer-formatter-14.8.4.tgz", - "integrity": "sha512-lpEaVF1iUBL4d+X04BIus7ubiPk5PeRmriFosxoCKT9RqJFXMnC6ApBGpWX5fLBTRK9XNesOpP0c+tXprOAPdw==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/fixer-formatter/-/fixer-formatter-15.2.1.tgz", + "integrity": "sha512-8TFs76YGJ+1rkIjQ5YXV4xhomYrkaMoqqST9d7UfGE1anj9koXNhSV4V8IHlK68dAQ91Qcc6Cdz/ohZ6Mv1t9Q==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/module-interop": "14.8.4", - "@textlint/resolver": "14.8.4", - "@textlint/types": "14.8.4", + "@textlint/module-interop": "15.2.1", + "@textlint/resolver": "15.2.1", + "@textlint/types": "15.2.1", "chalk": "^4.1.2", "debug": "^4.4.1", "diff": "^5.2.0", @@ -1442,36 +1463,36 @@ } }, "node_modules/@textlint/kernel": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/@textlint/kernel/-/kernel-14.8.4.tgz", - "integrity": "sha512-fBk8Lm4Ph7ogvqpSpRFiB0NM/rQVWOnOMLSJqZsdyvA40IVeZZYs+2bM1WgVdAZLUQTHSzKMExsHu2c91YVpKw==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/kernel/-/kernel-15.2.1.tgz", + "integrity": "sha512-Xen6wfOlZZtGPsXqk9enXlXxsDy3uXBQo+fENXohT0e6xnx500r4N6IrYOr/VANMoAS2DrIrqpN6Q1W1VpBZlg==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "14.8.4", - "@textlint/ast-tester": "14.8.4", - "@textlint/ast-traverse": "14.8.4", - "@textlint/feature-flag": "14.8.4", - "@textlint/source-code-fixer": "14.8.4", - "@textlint/types": "14.8.4", - "@textlint/utils": "14.8.4", + "@textlint/ast-node-types": "15.2.1", + "@textlint/ast-tester": "15.2.1", + "@textlint/ast-traverse": "15.2.1", + "@textlint/feature-flag": "15.2.1", + "@textlint/source-code-fixer": "15.2.1", + "@textlint/types": "15.2.1", + "@textlint/utils": "15.2.1", "debug": "^4.4.1", "fast-equals": "^4.0.3", "structured-source": "^4.0.0" } }, "node_modules/@textlint/linter-formatter": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-14.8.4.tgz", - "integrity": "sha512-sZ0UfYRDBNHnfMVBqLqqYnqTB7Ec169ljlmo+SEHR1T+dHUPYy1/DZK4p7QREXlBSFL4cnkswETCbc9xRodm4Q==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-15.2.1.tgz", + "integrity": "sha512-oollG/BHa07+mMt372amxHohteASC+Zxgollc1sZgiyxo4S6EuureV3a4QIQB0NecA+Ak3d0cl0WI/8nou38jw==", "dev": true, "license": "MIT", "dependencies": { "@azu/format-text": "^1.0.2", "@azu/style-format": "^1.0.1", - "@textlint/module-interop": "14.8.4", - "@textlint/resolver": "14.8.4", - "@textlint/types": "14.8.4", + "@textlint/module-interop": "15.2.1", + "@textlint/resolver": "15.2.1", + "@textlint/types": "15.2.1", "chalk": "^4.1.2", "debug": "^4.4.1", "js-yaml": "^3.14.1", @@ -1553,13 +1574,13 @@ } }, "node_modules/@textlint/markdown-to-ast": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/@textlint/markdown-to-ast/-/markdown-to-ast-14.8.4.tgz", - "integrity": "sha512-9x7xqpk//79nREP4Hb219UG3N3lERNorlhXOl1XX4A0y8BcDAKKDv70WftkF9VZ+sx4ys4dv/iOsBA29I0nNQA==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/markdown-to-ast/-/markdown-to-ast-15.2.1.tgz", + "integrity": "sha512-JU3AxIAuom/ZdKok6tkNVy8PsqjxKnSlBfld1dzyRV7/VFWbBuFdlaV6M8aJHa0HKy0Y9QHTXxiOJQkUPA5kWA==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "14.8.4", + "@textlint/ast-node-types": "15.2.1", "debug": "^4.4.1", "mdast-util-gfm-autolink-literal": "^0.1.3", "neotraverse": "^0.6.18", @@ -1572,76 +1593,76 @@ } }, "node_modules/@textlint/module-interop": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-14.8.4.tgz", - "integrity": "sha512-1LdPYLAVpa27NOt6EqvuFO99s4XLB0c19Hw9xKSG6xQ1K82nUEyuWhzTQKb3KJ5Qx7qj14JlXZLfnEuL6A16Bw==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-15.2.1.tgz", + "integrity": "sha512-b/C/ZNrm05n1ypymDknIcpkBle30V2ZgE3JVqQlA9PnQV46Ky510qrZk6s9yfKgA3m1YRnAw04m8xdVtqjq1qg==", "dev": true, "license": "MIT" }, "node_modules/@textlint/resolver": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-14.8.4.tgz", - "integrity": "sha512-nMDOgDAVwNU9ommh+Db0U+MCMNDPbQ/1HBNjbnHwxZkCpcT6hsAJwBe38CW/DtWVUv8yeR4R40IYNPT84srNwA==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.2.1.tgz", + "integrity": "sha512-FY3aK4tElEcOJVUsaMj4Zro4jCtKEEwUMIkDL0tcn6ljNcgOF7Em+KskRRk/xowFWayqDtdz5T3u7w/6fjjuJQ==", "dev": true, "license": "MIT" }, "node_modules/@textlint/source-code-fixer": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/@textlint/source-code-fixer/-/source-code-fixer-14.8.4.tgz", - "integrity": "sha512-/BTSLTgpRqrgwqB2Jmu/sRMEgB3sn9dxhDRmSX4hFFbtD2wT8/d4TcxD7rTe3NdWAPCCHQ8xCBUHDuZrTqDA4w==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/source-code-fixer/-/source-code-fixer-15.2.1.tgz", + "integrity": "sha512-wMjEcH2jWfCazj2paNo5S+mnpf/ephOWceNQ5Aq1jfWCcqyJEvF8xykiCW7iFiW/KVkVIUdTdcrb+ZgY1n5bzw==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/types": "14.8.4", + "@textlint/types": "15.2.1", "debug": "^4.4.1" } }, "node_modules/@textlint/text-to-ast": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/@textlint/text-to-ast/-/text-to-ast-14.8.4.tgz", - "integrity": "sha512-BWWEM12WqWUKmI9BQvnjtu4CElExWhm1asPE3j//jFTyR6oLv14NaFUaR26xGJWAI28WIa293AmWfE60ygHdRA==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/text-to-ast/-/text-to-ast-15.2.1.tgz", + "integrity": "sha512-m6j+35PfBB1pWRQ7oddKhZWMPIqeEdGvQDyFvr50piyjiJ4ME7ASVhfvA3yoLQ+92jW/DWRQ+gpj0tQPdlmq8Q==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "14.8.4" + "@textlint/ast-node-types": "15.2.1" } }, "node_modules/@textlint/textlint-plugin-markdown": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-markdown/-/textlint-plugin-markdown-14.8.4.tgz", - "integrity": "sha512-WWFo05mIsXaJPrWiR/nsvaLd/nUS0xWWeJg6AcpOkrxyIqH//PyTuQHD9sYpJkCFopWP1/8GeCba+a/m2llX4g==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-markdown/-/textlint-plugin-markdown-15.2.1.tgz", + "integrity": "sha512-mHWjpGsVeIgVkWeW+H9sMhxKN/FNVofbqwwERg0vxme37vEVKrAXAq9t25ZL5erco5qkvuWT55LmgKOQG48oXw==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/markdown-to-ast": "14.8.4", - "@textlint/types": "14.8.4" + "@textlint/markdown-to-ast": "15.2.1", + "@textlint/types": "15.2.1" } }, "node_modules/@textlint/textlint-plugin-text": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-text/-/textlint-plugin-text-14.8.4.tgz", - "integrity": "sha512-FY7H9a2I07/DzQtouQK9/Fs+9fgMAw5xQvHgAiqOffGU/i8WvWnsywflciW/IRi/By1TCd5nhdN/YRBvzuvfnw==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-text/-/textlint-plugin-text-15.2.1.tgz", + "integrity": "sha512-KI5vEEQXZJmEOrVuCeYVYHZLA6tWehwrvAYNjZLnlBICD8CTPLaFm3kqlDdGosbYnsk525BhEjW6sxAeA25a6g==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/text-to-ast": "14.8.4", - "@textlint/types": "14.8.4" + "@textlint/text-to-ast": "15.2.1", + "@textlint/types": "15.2.1" } }, "node_modules/@textlint/types": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/@textlint/types/-/types-14.8.4.tgz", - "integrity": "sha512-9nyY8vVXlr8hHKxa6+37omJhXWCwovMQcgMteuldYd4dOxGm14AK2nXdkgtKEUQnzLGaXy46xwLCfhQy7V7/YA==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.2.1.tgz", + "integrity": "sha512-zyqNhSatK1cwxDUgosEEN43hFh3WCty9Zm2Vm3ogU566IYegifwqN54ey/CiRy/DiO4vMcFHykuQnh2Zwp6LLw==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "14.8.4" + "@textlint/ast-node-types": "15.2.1" } }, "node_modules/@textlint/utils": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/@textlint/utils/-/utils-14.8.4.tgz", - "integrity": "sha512-ByRbUBtxhvZoI43CJJCy0oVPwpvB4/r8FhH33QguW9DSVk33y8ful5YIhV8ziSGjNJbwxGhe3rqR8YBmUkrnsQ==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/utils/-/utils-15.2.1.tgz", + "integrity": "sha512-osXG48HjqaOk21mX3upTmAMhEEIULjzZgH17S4pQzrU/16N0VsuiqYOZL14uN0gyWR8YY8lec+cszDN/eCwULg==", "dev": true, "license": "MIT" }, @@ -1696,6 +1717,13 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/unist": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", @@ -2416,16 +2444,6 @@ "node": ">= 0.8" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -2782,17 +2800,17 @@ "node": ">= 0.8" } }, - "node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", "dev": true, "license": "MIT", - "dependencies": { - "locate-path": "^2.0.0" - }, "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat-cache": { @@ -2987,13 +3005,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3038,11 +3049,17 @@ "license": "MIT" }, "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", "dev": true, - "license": "ISC" + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, "node_modules/html-escaper": { "version": "2.0.2", @@ -3091,6 +3108,19 @@ "node": ">=0.10.0" } }, + "node_modules/index-to-position": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.1.0.tgz", + "integrity": "sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -3134,13 +3164,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -3148,22 +3171,6 @@ "dev": true, "license": "MIT" }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-decimal": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", @@ -3213,13 +3220,6 @@ "dev": true, "license": "MIT" }, - "node_modules/is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", - "dev": true, - "license": "MIT" - }, "node_modules/isexe": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", @@ -3319,13 +3319,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true, - "license": "MIT" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3370,37 +3363,6 @@ "node": ">= 0.8.0" } }, - "node_modules/load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -3987,26 +3949,18 @@ } }, "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/object-assign": { @@ -4096,56 +4050,20 @@ } }, "node_modules/p-limit": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz", - "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.0.0.tgz", + "integrity": "sha512-WeCdPG5OjujcMWjSkOS0kt3bo+LmroXLmOnJ4SPhZfz5pffQxDUNcYscbZgyGwKf9r9z7gRfKjDNno5cZyQAZQ==", "license": "MIT", "dependencies": { - "yocto-queue": "^1.1.1" + "yocto-queue": "^1.2.1" }, "engines": { - "node": ">=18" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -4173,16 +4091,21 @@ } }, "node_modules/parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", "dev": true, "license": "MIT", "dependencies": { - "error-ex": "^1.2.0" + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/parseurl": { @@ -4195,16 +4118,6 @@ "node": ">= 0.8" } }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -4215,13 +4128,6 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", @@ -4256,21 +4162,6 @@ "node": ">=16" } }, - "node_modules/path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -4308,39 +4199,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/pkce-challenge": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", @@ -4476,111 +4334,42 @@ "require-from-string": "^2.0.2" } }, - "node_modules/read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "node_modules/read-package-up": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", + "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==", "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" + "find-up-simple": "^1.0.0", + "read-pkg": "^9.0.0", + "type-fest": "^4.6.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "license": "MIT", - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "node": ">=18" }, - "engines": { - "node": ">=4" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg-up/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", "dev": true, "license": "MIT", "dependencies": { - "pify": "^3.0.0" + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "node": ">=18" }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/remark-footnotes": { @@ -4662,27 +4451,6 @@ "node": ">=0.10.0" } }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", @@ -5155,19 +4923,6 @@ "node": ">=8" } }, - "node_modules/strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-utf8": "^0.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/strip-json-comments": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", @@ -5217,19 +4972,6 @@ "node": ">=8" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/table": { "version": "6.9.0", "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", @@ -5355,45 +5097,44 @@ "license": "MIT" }, "node_modules/textlint": { - "version": "14.8.4", - "resolved": "https://registry.npmjs.org/textlint/-/textlint-14.8.4.tgz", - "integrity": "sha512-oV7DwKjdbIk+5LlAhtTtWsudzNdUnEpP2KW2iIRnjdZ0uM/vXhffDh66UL6P3nk7Io37qhSRb3E82fdVHqyblw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@modelcontextprotocol/sdk": "^1.12.1", - "@textlint/ast-node-types": "14.8.4", - "@textlint/ast-traverse": "14.8.4", - "@textlint/config-loader": "14.8.4", - "@textlint/feature-flag": "14.8.4", - "@textlint/fixer-formatter": "14.8.4", - "@textlint/kernel": "14.8.4", - "@textlint/linter-formatter": "14.8.4", - "@textlint/module-interop": "14.8.4", - "@textlint/resolver": "14.8.4", - "@textlint/textlint-plugin-markdown": "14.8.4", - "@textlint/textlint-plugin-text": "14.8.4", - "@textlint/types": "14.8.4", - "@textlint/utils": "14.8.4", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/textlint/-/textlint-15.2.1.tgz", + "integrity": "sha512-FChEKsi81lcjv9ZucXpbMNA3HQw27eafUw0F7fyiX56u8eNr2rSb5oAtZO0DF6a+/bNDe8W7NG3BxOAPsWFoDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.15.1", + "@textlint/ast-node-types": "15.2.1", + "@textlint/ast-traverse": "15.2.1", + "@textlint/config-loader": "15.2.1", + "@textlint/feature-flag": "15.2.1", + "@textlint/fixer-formatter": "15.2.1", + "@textlint/kernel": "15.2.1", + "@textlint/linter-formatter": "15.2.1", + "@textlint/module-interop": "15.2.1", + "@textlint/resolver": "15.2.1", + "@textlint/textlint-plugin-markdown": "15.2.1", + "@textlint/textlint-plugin-text": "15.2.1", + "@textlint/types": "15.2.1", + "@textlint/utils": "15.2.1", "debug": "^4.4.1", - "file-entry-cache": "^10.0.8", + "file-entry-cache": "^10.1.1", "glob": "^10.4.5", "md5": "^2.3.0", "mkdirp": "^0.5.6", "optionator": "^0.9.4", "path-to-glob-pattern": "^2.0.1", "rc-config-loader": "^4.1.3", - "read-pkg": "^1.1.0", - "read-pkg-up": "^3.0.0", + "read-package-up": "^11.0.0", "structured-source": "^4.0.0", "unique-concat": "^0.2.2", - "zod": "^3.25.56" + "zod": "^3.25.76" }, "bin": { "textlint": "bin/textlint.js" }, "engines": { - "node": ">=18.14.0" + "node": ">=20.0.0" } }, "node_modules/textlint-filter-rule-comments": { @@ -5418,13 +5159,6 @@ "unist-util-visit": "^2.0.3" } }, - "node_modules/textlint-rule-helper/node_modules/@textlint/ast-node-types": { - "version": "15.2.1", - "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.2.1.tgz", - "integrity": "sha512-20fEcLPsXg81yWpApv4FQxrZmlFF/Ta7/kz1HGIL+pJo5cSTmkc+eCki3GpOPZIoZk0tbJU8hrlwUb91F+3SNQ==", - "dev": true, - "license": "MIT" - }, "node_modules/textlint-rule-terminology": { "version": "5.2.14", "resolved": "https://registry.npmjs.org/textlint-rule-terminology/-/textlint-rule-terminology-5.2.14.tgz", @@ -5574,6 +5308,19 @@ "node": ">= 0.8.0" } }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -5622,6 +5369,19 @@ "dev": true, "license": "MIT" }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unified": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", diff --git a/package.json b/package.json index bf9fb129..8d372ea0 100644 --- a/package.json +++ b/package.json @@ -54,11 +54,11 @@ "@octokit/plugin-rest-endpoint-methods": "^16.0.0", "@octokit/request-error": "^7.0.0", "minimatch": "^10.0.1", - "p-limit": "^6.2.0", + "p-limit": "^7.0.0", "which": "^5.0.0" }, "devDependencies": { - "@biomejs/biome": "^1.9.4", + "@biomejs/biome": "^2.2.0", "@octokit/types": "^14.0.0", "@octokit/webhooks-types": "^7.6.1", "@types/js-yaml": "^4.0.9", @@ -69,7 +69,7 @@ "js-yaml": "^4.1.0", "make-coverage-badge": "^1.2.0", "openai": "latest", - "textlint": "^14.8.0", + "textlint": "^15.2.1", "textlint-filter-rule-comments": "^1.2.2", "textlint-rule-terminology": "^5.2.12", "ts-deepmerge": "^7.0.2", diff --git a/scripts/dev-parse-modules.ts b/scripts/dev-parse-modules.ts index e395c7fa..f472f80e 100644 --- a/scripts/dev-parse-modules.ts +++ b/scripts/dev-parse-modules.ts @@ -37,11 +37,11 @@ async function main() { process.env['INPUT_MODULE-CHANGE-EXCLUDE-PATTERNS'] = '.gitignore,*.md'; // Initialize - const config = getConfig(); - const context = getContext(); + const _config = getConfig(); + const _context = getContext(); // Test with empty tags and releases for now - const modules = parseTerraformModules( + const _modules = parseTerraformModules( [ { message: 'feat: add screenshots for documentation', diff --git a/src/config.ts b/src/config.ts index e116d9fc..f5e21e8e 100644 --- a/src/config.ts +++ b/src/config.ts @@ -107,7 +107,7 @@ export function getConfig(): Config { // For backward compatibility and existing usage export const config: Config = new Proxy({} as Config, { - get(target, prop) { + get(_target, prop) { return getConfig()[prop as keyof Config]; }, }); diff --git a/src/context.ts b/src/context.ts index a4d76a65..0da5df19 100644 --- a/src/context.ts +++ b/src/context.ts @@ -165,7 +165,7 @@ export const getContext = (): Context => { // For backward compatibility and existing usage export const context: Context = new Proxy({} as Context, { - get(target, prop) { + get(_target, prop) { return getContext()[prop as keyof Context]; }, }); From de13c5a1b3104cf2447140a922a9a00796ff95ce Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Tue, 19 Aug 2025 00:27:27 +0000 Subject: [PATCH 20/29] fix: update @types/node to version 22.17.2 for improved compatibility --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index d6a3a70b..e146f53d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "@octokit/types": "^14.0.0", "@octokit/webhooks-types": "^7.6.1", "@types/js-yaml": "^4.0.9", - "@types/node": "^22.15.29", + "@types/node": "^22.17.2", "@types/which": "^3.0.4", "@vercel/ncc": "^0.38.3", "@vitest/coverage-v8": "^3.1.3", diff --git a/package.json b/package.json index 8d372ea0..c1a52f13 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "@octokit/types": "^14.0.0", "@octokit/webhooks-types": "^7.6.1", "@types/js-yaml": "^4.0.9", - "@types/node": "^22.15.29", + "@types/node": "^22.17.2", "@types/which": "^3.0.4", "@vercel/ncc": "^0.38.3", "@vitest/coverage-v8": "^3.1.3", From 88565fe15c1e4f308eb420683e890763d5a87c3d Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Tue, 19 Aug 2025 00:37:09 +0000 Subject: [PATCH 21/29] fix: update GitHub token reference for CI testing in workflow --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9f0491d8..61c699af 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,7 +33,7 @@ jobs: - name: Run Tests Typescript run: npm run test env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN_REPO_CI_TESTING }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} sonarqube-scan: runs-on: ubuntu-latest From 9a602360416f21e7cc7cc50be780d8145accccc8 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Tue, 19 Aug 2025 00:45:04 +0000 Subject: [PATCH 22/29] refactor: simplify ACTION_INPUTS metadata definitions using factory functions --- src/utils/metadata.ts | 130 ++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 80 deletions(-) diff --git a/src/utils/metadata.ts b/src/utils/metadata.ts index 0856b9f5..aca1fb66 100644 --- a/src/utils/metadata.ts +++ b/src/utils/metadata.ts @@ -1,92 +1,62 @@ import type { ActionInputMetadata, Config } from '@/types'; import { getBooleanInput, getInput } from '@actions/core'; +/** + * Factory functions to reduce duplication in ACTION_INPUTS metadata definitions. + * These functions create standardized metadata objects for common input patterns. + */ +const requiredString = (configKey: keyof Config): ActionInputMetadata => ({ + configKey, + required: true, + type: 'string', +}); + +const requiredBoolean = (configKey: keyof Config): ActionInputMetadata => ({ + configKey, + required: true, + type: 'boolean', +}); + +const requiredArray = (configKey: keyof Config): ActionInputMetadata => ({ + configKey, + required: true, + type: 'array', +}); + +const requiredNumber = (configKey: keyof Config): ActionInputMetadata => ({ + configKey, + required: true, + type: 'number', +}); + +const optionalArray = (configKey: keyof Config): ActionInputMetadata => ({ + configKey, + required: false, + type: 'array', +}); + /** * Complete mapping of all GitHub Action inputs to their metadata. * This is the single source of truth for input configuration. * Note: defaultValue is removed as defaults come from action.yml at runtime */ export const ACTION_INPUTS: Record = { - 'major-keywords': { - configKey: 'majorKeywords', - required: true, - type: 'array', - }, - 'minor-keywords': { - configKey: 'minorKeywords', - required: true, - type: 'array', - }, - 'patch-keywords': { - configKey: 'patchKeywords', - required: true, - type: 'array', - }, - 'default-first-tag': { - configKey: 'defaultFirstTag', - required: true, - type: 'string', - }, - 'terraform-docs-version': { - configKey: 'terraformDocsVersion', - required: true, - type: 'string', - }, - 'delete-legacy-tags': { - configKey: 'deleteLegacyTags', - required: true, - type: 'boolean', - }, - 'disable-wiki': { - configKey: 'disableWiki', - required: true, - type: 'boolean', - }, - 'wiki-sidebar-changelog-max': { - configKey: 'wikiSidebarChangelogMax', - required: true, - type: 'number', - }, - 'disable-branding': { - configKey: 'disableBranding', - required: true, - type: 'boolean', - }, - 'module-path-ignore': { - configKey: 'modulePathIgnore', - required: false, - type: 'array', - }, - 'module-change-exclude-patterns': { - configKey: 'moduleChangeExcludePatterns', - required: false, - type: 'array', - }, - 'module-asset-exclude-patterns': { - configKey: 'moduleAssetExcludePatterns', - required: false, - type: 'array', - }, - 'use-ssh-source-format': { - configKey: 'useSSHSourceFormat', - required: true, - type: 'boolean', - }, - github_token: { - configKey: 'githubToken', - required: true, - type: 'string', - }, - 'tag-directory-separator': { - configKey: 'tagDirectorySeparator', - required: true, - type: 'string', - }, - 'use-version-prefix': { - configKey: 'useVersionPrefix', - required: true, - type: 'boolean', - }, + 'major-keywords': requiredArray('majorKeywords'), + 'minor-keywords': requiredArray('minorKeywords'), + 'patch-keywords': requiredArray('patchKeywords'), + 'default-first-tag': requiredString('defaultFirstTag'), + 'terraform-docs-version': requiredString('terraformDocsVersion'), + 'delete-legacy-tags': requiredBoolean('deleteLegacyTags'), + 'disable-wiki': requiredBoolean('disableWiki'), + 'wiki-sidebar-changelog-max': requiredNumber('wikiSidebarChangelogMax'), + 'disable-branding': requiredBoolean('disableBranding'), + 'module-path-ignore': optionalArray('modulePathIgnore'), + 'module-change-exclude-patterns': optionalArray('moduleChangeExcludePatterns'), + 'module-asset-exclude-patterns': optionalArray('moduleAssetExcludePatterns'), + 'use-ssh-source-format': requiredBoolean('useSSHSourceFormat'), + github_token: requiredString('githubToken'), + 'tag-directory-separator': requiredString('tagDirectorySeparator'), + 'use-version-prefix': requiredBoolean('useVersionPrefix'), } as const; /** From d62ca15bf455341eb8794a08008e97bed16eb485 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Tue, 19 Aug 2025 00:50:12 +0000 Subject: [PATCH 23/29] test: enhance ACTION_INPUTS tests for metadata structure and type safety --- __tests__/utils/metadata.test.ts | 224 +++++++++++++++++++++++++++++-- 1 file changed, 212 insertions(+), 12 deletions(-) diff --git a/__tests__/utils/metadata.test.ts b/__tests__/utils/metadata.test.ts index 755d8101..633bf202 100644 --- a/__tests__/utils/metadata.test.ts +++ b/__tests__/utils/metadata.test.ts @@ -1,23 +1,223 @@ -import { createConfigFromInputs } from '@/utils/metadata'; -import { getInput } from '@actions/core'; +import { ACTION_INPUTS, createConfigFromInputs } from '@/utils/metadata'; +import type { ActionInputMetadata } from '@/types'; +import { getBooleanInput, getInput } from '@actions/core'; import { describe, expect, it, vi } from 'vitest'; describe('utils/metadata', () => { - it('should throw a custom error if getInput fails', () => { - const errorMessage = 'Input retrieval failed'; - vi.mocked(getInput).mockImplementation(() => { - throw new Error(errorMessage); + describe('ACTION_INPUTS', () => { + it('should contain all expected input configurations', () => { + const expectedInputs = [ + 'major-keywords', + 'minor-keywords', + 'patch-keywords', + 'default-first-tag', + 'terraform-docs-version', + 'delete-legacy-tags', + 'disable-wiki', + 'wiki-sidebar-changelog-max', + 'disable-branding', + 'module-path-ignore', + 'module-change-exclude-patterns', + 'module-asset-exclude-patterns', + 'use-ssh-source-format', + 'github_token', + 'tag-directory-separator', + 'use-version-prefix', + ]; + + expect(Object.keys(ACTION_INPUTS)).toEqual(expect.arrayContaining(expectedInputs)); + expect(Object.keys(ACTION_INPUTS)).toHaveLength(expectedInputs.length); + }); + + it('should have correct metadata structure for required string inputs', () => { + const stringInputs = ['default-first-tag', 'terraform-docs-version', 'github_token', 'tag-directory-separator']; + + for (const inputName of stringInputs) { + const metadata = ACTION_INPUTS[inputName]; + expect(metadata).toEqual({ + configKey: expect.any(String), + required: true, + type: 'string', + }); + } + }); + + it('should have correct metadata structure for required boolean inputs', () => { + const booleanInputs = [ + 'delete-legacy-tags', + 'disable-wiki', + 'disable-branding', + 'use-ssh-source-format', + 'use-version-prefix', + ]; + + for (const inputName of booleanInputs) { + const metadata = ACTION_INPUTS[inputName]; + expect(metadata).toEqual({ + configKey: expect.any(String), + required: true, + type: 'boolean', + }); + } + }); + + it('should have correct metadata structure for required array inputs', () => { + const arrayInputs = ['major-keywords', 'minor-keywords', 'patch-keywords']; + + for (const inputName of arrayInputs) { + const metadata = ACTION_INPUTS[inputName]; + expect(metadata).toEqual({ + configKey: expect.any(String), + required: true, + type: 'array', + }); + } + }); + + it('should have correct metadata structure for required number inputs', () => { + const numberInputs = ['wiki-sidebar-changelog-max']; + + for (const inputName of numberInputs) { + const metadata = ACTION_INPUTS[inputName]; + expect(metadata).toEqual({ + configKey: expect.any(String), + required: true, + type: 'number', + }); + } }); - expect(() => createConfigFromInputs()).toThrow(`Failed to process input 'major-keywords': ${errorMessage}`); + it('should have correct metadata structure for optional array inputs', () => { + const optionalArrayInputs = [ + 'module-path-ignore', + 'module-change-exclude-patterns', + 'module-asset-exclude-patterns', + ]; + + for (const inputName of optionalArrayInputs) { + const metadata = ACTION_INPUTS[inputName]; + expect(metadata).toEqual({ + configKey: expect.any(String), + required: false, + type: 'array', + }); + } + }); + + it('should have proper configKey mappings', () => { + const expectedMappings: Record = { + 'major-keywords': 'majorKeywords', + 'minor-keywords': 'minorKeywords', + 'patch-keywords': 'patchKeywords', + 'default-first-tag': 'defaultFirstTag', + 'terraform-docs-version': 'terraformDocsVersion', + 'delete-legacy-tags': 'deleteLegacyTags', + 'disable-wiki': 'disableWiki', + 'wiki-sidebar-changelog-max': 'wikiSidebarChangelogMax', + 'disable-branding': 'disableBranding', + 'module-path-ignore': 'modulePathIgnore', + 'module-change-exclude-patterns': 'moduleChangeExcludePatterns', + 'module-asset-exclude-patterns': 'moduleAssetExcludePatterns', + 'use-ssh-source-format': 'useSSHSourceFormat', + 'github_token': 'githubToken', + 'tag-directory-separator': 'tagDirectorySeparator', + 'use-version-prefix': 'useVersionPrefix', + }; + + for (const [inputName, expectedConfigKey] of Object.entries(expectedMappings)) { + expect(ACTION_INPUTS[inputName].configKey).toBe(expectedConfigKey); + } + }); + + it('should maintain type safety with ActionInputMetadata interface', () => { + // This test ensures the factory functions create valid ActionInputMetadata objects + for (const metadata of Object.values(ACTION_INPUTS)) { + expect(metadata).toEqual( + expect.objectContaining({ + configKey: expect.any(String), + required: expect.any(Boolean), + type: expect.stringMatching(/^(string|boolean|array|number)$/), + }) + ); + + // Ensure type is properly typed + const validTypes: ActionInputMetadata['type'][] = ['string', 'boolean', 'array', 'number']; + expect(validTypes).toContain(metadata.type); + } + }); }); - it('should handle non-Error objects thrown during input processing', () => { - const errorObject = 'A plain string error'; - vi.mocked(getInput).mockImplementation(() => { - throw errorObject; + describe('createConfigFromInputs', () => { + it('should throw a custom error if getInput fails', () => { + const errorMessage = 'Input retrieval failed'; + vi.mocked(getInput).mockImplementation(() => { + throw new Error(errorMessage); + }); + + expect(() => createConfigFromInputs()).toThrow(`Failed to process input 'major-keywords': ${errorMessage}`); }); - expect(() => createConfigFromInputs()).toThrow(`Failed to process input 'major-keywords': ${String(errorObject)}`); + it('should handle non-Error objects thrown during input processing', () => { + const errorObject = 'A plain string error'; + vi.mocked(getInput).mockImplementation(() => { + throw errorObject; + }); + + expect(() => createConfigFromInputs()).toThrow(`Failed to process input 'major-keywords': ${String(errorObject)}`); + }); + + it('should process all input types correctly', () => { + // Mock the GitHub Actions core functions + vi.mocked(getInput).mockImplementation((name) => { + const mockValues: Record = { + 'major-keywords': 'breaking,major', + 'minor-keywords': 'feat,feature', + 'patch-keywords': 'fix,chore', + 'default-first-tag': 'v1.0.0', + 'terraform-docs-version': 'v0.20.0', + 'wiki-sidebar-changelog-max': '5', + 'module-path-ignore': '', + 'module-change-exclude-patterns': '*.md,tests/**', + 'module-asset-exclude-patterns': '*.md,tests/**', + 'github_token': 'fake-token', + 'tag-directory-separator': '/', + 'use-ssh-source-format': 'false', + }; + return mockValues[name] || ''; + }); + + vi.mocked(getBooleanInput).mockImplementation((name) => { + const mockBooleans: Record = { + 'delete-legacy-tags': true, + 'disable-wiki': false, + 'disable-branding': false, + 'use-ssh-source-format': false, + 'use-version-prefix': true, + }; + return mockBooleans[name] || false; + }); + + const config = createConfigFromInputs(); + + // Verify all config properties are set + expect(config).toEqual({ + majorKeywords: ['breaking', 'major'], + minorKeywords: ['feat', 'feature'], + patchKeywords: ['fix', 'chore'], + defaultFirstTag: 'v1.0.0', + terraformDocsVersion: 'v0.20.0', + deleteLegacyTags: true, + disableWiki: false, + wikiSidebarChangelogMax: 5, + disableBranding: false, + modulePathIgnore: [], + moduleChangeExcludePatterns: ['*.md', 'tests/**'], + moduleAssetExcludePatterns: ['*.md', 'tests/**'], + useSSHSourceFormat: false, + githubToken: 'fake-token', + tagDirectorySeparator: '/', + useVersionPrefix: true, + }); + }); }); }); From 8b8149ed1bf304d936b63abb2ecb57639600a87b Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Tue, 19 Aug 2025 00:54:49 +0000 Subject: [PATCH 24/29] fix: formatting --- __tests__/utils/metadata.test.ts | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/__tests__/utils/metadata.test.ts b/__tests__/utils/metadata.test.ts index 633bf202..53e9c977 100644 --- a/__tests__/utils/metadata.test.ts +++ b/__tests__/utils/metadata.test.ts @@ -8,7 +8,7 @@ describe('utils/metadata', () => { it('should contain all expected input configurations', () => { const expectedInputs = [ 'major-keywords', - 'minor-keywords', + 'minor-keywords', 'patch-keywords', 'default-first-tag', 'terraform-docs-version', @@ -31,7 +31,7 @@ describe('utils/metadata', () => { it('should have correct metadata structure for required string inputs', () => { const stringInputs = ['default-first-tag', 'terraform-docs-version', 'github_token', 'tag-directory-separator']; - + for (const inputName of stringInputs) { const metadata = ACTION_INPUTS[inputName]; expect(metadata).toEqual({ @@ -45,12 +45,12 @@ describe('utils/metadata', () => { it('should have correct metadata structure for required boolean inputs', () => { const booleanInputs = [ 'delete-legacy-tags', - 'disable-wiki', + 'disable-wiki', 'disable-branding', 'use-ssh-source-format', 'use-version-prefix', ]; - + for (const inputName of booleanInputs) { const metadata = ACTION_INPUTS[inputName]; expect(metadata).toEqual({ @@ -63,7 +63,7 @@ describe('utils/metadata', () => { it('should have correct metadata structure for required array inputs', () => { const arrayInputs = ['major-keywords', 'minor-keywords', 'patch-keywords']; - + for (const inputName of arrayInputs) { const metadata = ACTION_INPUTS[inputName]; expect(metadata).toEqual({ @@ -76,7 +76,7 @@ describe('utils/metadata', () => { it('should have correct metadata structure for required number inputs', () => { const numberInputs = ['wiki-sidebar-changelog-max']; - + for (const inputName of numberInputs) { const metadata = ACTION_INPUTS[inputName]; expect(metadata).toEqual({ @@ -93,7 +93,7 @@ describe('utils/metadata', () => { 'module-change-exclude-patterns', 'module-asset-exclude-patterns', ]; - + for (const inputName of optionalArrayInputs) { const metadata = ACTION_INPUTS[inputName]; expect(metadata).toEqual({ @@ -119,7 +119,7 @@ describe('utils/metadata', () => { 'module-change-exclude-patterns': 'moduleChangeExcludePatterns', 'module-asset-exclude-patterns': 'moduleAssetExcludePatterns', 'use-ssh-source-format': 'useSSHSourceFormat', - 'github_token': 'githubToken', + github_token: 'githubToken', 'tag-directory-separator': 'tagDirectorySeparator', 'use-version-prefix': 'useVersionPrefix', }; @@ -137,9 +137,9 @@ describe('utils/metadata', () => { configKey: expect.any(String), required: expect.any(Boolean), type: expect.stringMatching(/^(string|boolean|array|number)$/), - }) + }), ); - + // Ensure type is properly typed const validTypes: ActionInputMetadata['type'][] = ['string', 'boolean', 'array', 'number']; expect(validTypes).toContain(metadata.type); @@ -163,7 +163,9 @@ describe('utils/metadata', () => { throw errorObject; }); - expect(() => createConfigFromInputs()).toThrow(`Failed to process input 'major-keywords': ${String(errorObject)}`); + expect(() => createConfigFromInputs()).toThrow( + `Failed to process input 'major-keywords': ${String(errorObject)}`, + ); }); it('should process all input types correctly', () => { @@ -179,7 +181,7 @@ describe('utils/metadata', () => { 'module-path-ignore': '', 'module-change-exclude-patterns': '*.md,tests/**', 'module-asset-exclude-patterns': '*.md,tests/**', - 'github_token': 'fake-token', + github_token: 'fake-token', 'tag-directory-separator': '/', 'use-ssh-source-format': 'false', }; From 0bfd6eda587e011ea12e63303559e2993ec13739 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Tue, 19 Aug 2025 01:05:42 +0000 Subject: [PATCH 25/29] test: add tests for VALID_TAG_DIRECTORY_SEPARATORS, VERSION_TAG_REGEX, and MODULE_TAG_REGEX --- __tests__/utils/constants.test.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/__tests__/utils/constants.test.ts b/__tests__/utils/constants.test.ts index af782714..abbde3bd 100644 --- a/__tests__/utils/constants.test.ts +++ b/__tests__/utils/constants.test.ts @@ -1,4 +1,7 @@ import { + VALID_TAG_DIRECTORY_SEPARATORS, + VERSION_TAG_REGEX, + MODULE_TAG_REGEX, BRANDING_COMMENT, BRANDING_WIKI, GITHUB_ACTIONS_BOT_NAME, @@ -11,6 +14,18 @@ import { import { describe, expect, it } from 'vitest'; describe('utils/constants', () => { + it('should have the correct default separators', () => { + expect(VALID_TAG_DIRECTORY_SEPARATORS).toStrictEqual(['-', '_', '/', '.']); + }); + + it('should have the correct version tag regex', () => { + expect(VERSION_TAG_REGEX).toStrictEqual(/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/); + }); + + it('should have the correct module tag regex', () => { + expect(MODULE_TAG_REGEX).toStrictEqual(/^(.+)([-_/.])(v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*))$/); + }); + it('should have the correct GitHub Actions bot name', () => { expect(GITHUB_ACTIONS_BOT_NAME).toBe('GitHub Actions'); }); From 6fe0e7cdbad527e966303dcce7bfd74275b28658 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Tue, 19 Aug 2025 01:15:46 +0000 Subject: [PATCH 26/29] refactor: remove redundant SonarQube job steps from workflow --- .github/workflows/test.yml | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 61c699af..82544f88 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ on: types: [opened, synchronize, reopened] jobs: - typescript-tests: + Tests: runs-on: ubuntu-latest name: TypeScript Tests permissions: @@ -35,26 +35,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - sonarqube-scan: - runs-on: ubuntu-latest - name: SonarQube Analysis - permissions: - contents: read - steps: - - uses: actions/checkout@v4 - with: - # Disabling shallow clone is recommended for improving relevancy of reporting - fetch-depth: 0 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version-file: .node-version - cache: npm - - - name: Install Dependencies - run: npm ci --no-fund - + # Note: SonarQube requires the results from tests to get the coverage report - name: SonarQube Scan uses: SonarSource/sonarqube-scan-action@2500896589ef8f7247069a56136f8dc177c27ccf # v5 env: From b33ab14184ca09a08babfedb2124162e8dc0f367 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Thu, 21 Aug 2025 01:21:46 +0000 Subject: [PATCH 27/29] feat: refactor render -> renderTemplate function and update wiki usage template handling --- .github/workflows/ci.yml | 4 +- README.md | 63 +++++++----- __tests__/templating.test.ts | 39 -------- __tests__/utils/metadata.test.ts | 57 +---------- __tests__/utils/string.test.ts | 88 ++++++++++++++++- __tests__/wiki.test.ts | 5 +- action.yml | 26 ++--- scripts/action.yml | 163 +++++++++++++++++++++++++++++++ src/templating.ts | 12 --- src/types/config.types.ts | 11 +++ src/utils/metadata.ts | 1 + src/utils/string.ts | 21 ++++ src/wiki.ts | 29 +++--- tf-modules/vpc-endpoint/main.tf | 1 + 14 files changed, 354 insertions(+), 166 deletions(-) delete mode 100644 __tests__/templating.test.ts create mode 100644 scripts/action.yml delete mode 100644 src/templating.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9524be92..df1cced5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,8 +49,8 @@ jobs: disable-wiki: false wiki-sidebar-changelog-max: 10 delete-legacy-tags: false # Note: We don't want to delete tags in this repository - terraform-docs-version: v0.19.0 - module-path-ignore: tf-modules/kms/examples/complete + terraform-docs-version: v0.20.0 + module-path-ignore: tf-modules/kms/examples/complete,tf-modules/zoo,tf-modules/animal,tf-modules/s3-bucket-object module-change-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/**,examples/** module-asset-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/** use-ssh-source-format: true diff --git a/README.md b/README.md index 44577c50..66cf6eef 100644 --- a/README.md +++ b/README.md @@ -194,12 +194,12 @@ configuring the following optional input parameters as needed. | `delete-legacy-tags` | Specifies a boolean that determines whether tags and releases from Terraform modules that have been deleted should be automatically removed | `true` | | `disable-wiki` | Whether to disable wiki generation for Terraform modules | `false` | | `wiki-sidebar-changelog-max` | An integer that specifies how many changelog entries are displayed in the sidebar per module | `5` | -| `wiki-usage-template` | A raw, multi-line string to override the default 'Usage' section in the generated wiki. Allows using variables like {{module_name}}, {{latest_tag}}, {{latest_tag_version_number}} and more.
[Read more here](#configuring-the-usage-template) | [See action.yml](https://github.com/polleuretan/terraform-module-releaser/blob/main/action.yml#L108) | +| `wiki-usage-template` | A raw, multi-line string to override the default 'Usage' section in the generated wiki. Allows using variables like {{module_name}}, {{latest_tag}}, {{latest_tag_version_number}} and more.
[Read more here](#configuring-the-wiki-usage-template) | [See action.yml](https://github.com/polleuretan/terraform-module-releaser/blob/main/action.yml#L108) | | `disable-branding` | Controls whether a small branding link to the action's repository is added to PR comments. Recommended to leave enabled to support OSS. | `false` | | `module-path-ignore` | Comma-separated list of module paths to completely ignore. Modules matching any pattern here are excluded from all versioning, releases, and documentation.
[Read more here](#understanding-the-filtering-options) | `` (empty string) | | `module-change-exclude-patterns` | Comma-separated list of file patterns (relative to each module) to exclude from triggering version changes. Lets you release a module but control which files inside it do not force a version bump.
[Read more here](#understanding-the-filtering-options) | `.gitignore,*.md,*.tftest.hcl,tests/**` | | `module-asset-exclude-patterns` | A comma-separated list of file patterns to exclude when bundling a Terraform module for tag/release. Patterns follow glob syntax (e.g., `tests/\*\*`) and are relative to each Terraform module directory. Files matching these patterns will be excluded from the bundled output. | `.gitignore,*.md,*.tftest.hcl,tests/**` | -| `use-ssh-source-format` | If enabled, all links to source code in generated Wiki documentation will use SSH standard format (e.g., `git::ssh://git@github.com/owner/repo.git`) instead of HTTPS format (`git::https://github.com/owner/repo.git`) | `false` | +| `use-ssh-source-format` | If enabled, all links to source code in generated Wiki documentation will use SSH format (e.g., `git::ssh://git@github.com/owner/repo.git`) instead of HTTPS format (`git::https://github.com/owner/repo.git`) | `false` | | `tag-directory-separator` | Character used to separate directory path components in Git tags. Supports `/`, `-`, `_`, or `.` | `/` | | `use-version-prefix` | Whether to include the 'v' prefix on version tags (e.g., v1.2.3 vs 1.2.3) | `true` | @@ -284,18 +284,18 @@ similar to those used in `.gitignore` files. For more details on the pattern mat [source code](https://github.com/techpivot/terraform-module-releaser/blob/main/src/utils/file.ts) or visit the [minimatch documentation](https://github.com/isaacs/minimatch). -### Configuring the Usage Template +### Configuring the Wiki Usage Template The `wiki-usage-template` input allows you to customize the "Usage" section of the generated wiki page for each module. You can use the following dynamic variables in your template: -| Variable | Description | Example | -| ------------------------------- | ------------------------------------------------------------------------------------------------- | ---------------------------------------- | -| `{{module_name}}` | The name of the module. | `aws/s3-bucket` | -| `{{latest_tag}}` | The latest Git tag for the module. | `aws/s3-bucket/v1.2.3` | -| `{{latest_tag_version_number}}` | The version number of the latest tag. | `1.2.3` | -| `{{module_source}}` | The Git source URL for the module, respecting the `use-ssh-source-format` input. | `git::https://github.com/owner/repo.git` | -| `{{module_name_terraform}}` | A Terraform-safe version of the module name (e.g., special characters replaced with underscores). | `aws_s3_bucket` | +| Variable | Description | Example | +| ------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | +| `{{module_name}}` | The name of the module. | `aws/s3-bucket` | +| `{{latest_tag}}` | The latest Git tag for the module. | `aws/s3-bucket/v1.2.3` | +| `{{latest_tag_version_number}}` | The version number of the latest tag (Always excludes any `v` prefix) | `1.2.3` | +| `{{module_source}}` | The Git source URL for the module with `git::` prefix, respecting the `use-ssh-source-format` input. | `git::ssh://github.com/techpivot/terraform-module-releaser.git` | +| `{{module_name_terraform}}` | A Terraform-safe version of the module name (e.g., special characters replaced with underscores). | `aws_s3_bucket` | ### Example Usage with Inputs @@ -325,29 +325,31 @@ jobs: minor-keywords: feat,feature patch-keywords: fix,chore,docs default-first-tag: v1.0.0 - terraform-docs-version: v0.19.0 + terraform-docs-version: v0.20.0 delete-legacy-tags: true disable-wiki: false wiki-sidebar-changelog-max: 10 module-path-ignore: path/to/ignore1,path/to/ignore2 module-change-exclude-patterns: .gitignore,*.md,docs/**,examples/**,*.tftest.hcl,tests/** module-asset-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/** - use-ssh-source-format: false + use-ssh-source-format: true + tag-directory-separator: / + use-version-prefix: true wiki-usage-template: | - # My Custom Usage Instructions + This is a custom wiki usage block that supports markdown. - This is a custom usage block. - - You can add any markdown you want here. - - And use variables like {{module_name}}, {{latest_tag}}, {{latest_tag_version_number}}, - {{module_source}} and {{module_name_terraform}}. + The following variables are supported: + - {{module_name}} + - {{latest_tag}} + - {{latest_tag_version_number}} + - {{module_source}} + - {{module_name_terraform}} ```hcl module "{{module_name_terraform}}" { - source = "{{module_source}}?ref={{latest_tag}}" - version = "{{latest_tag_version_number}}" - # ... + source = "{{module_source}}?ref={{latest_tag}}" + + # See inputs below for additional required parameters } ``` ```` @@ -369,6 +371,8 @@ The following outputs are available from this action: ```json { + "changed-module-names": ["aws/vpc"], + "changed-module-paths": ["/home/runner/work/terraform-module-releaser/terraform-module-releaser/aws/vpc"], "changed-modules-map": { "aws/vpc": { "path": "modules/aws/vpc", @@ -377,16 +381,21 @@ The following outputs are available from this action: "releaseType": "minor" } }, + "all-module-names": ["aws/s3", "aws/vpc"], + "all-module-paths": [ + "/home/runner/work/terraform-module-releaser/terraform-module-releaser/aws/s3", + "/home/runner/work/terraform-module-releaser/terraform-module-releaser/aws/vpc" + ], "all-modules-map": { - "aws/vpc": { - "path": "modules/aws/vpc", - "latestTag": "aws/vpc/v1.0.0", - "latestTagVersion": "v1.0.0" - }, "aws/s3": { "path": "modules/aws/s3", "latestTag": "aws/s3/v2.1.0", "latestTagVersion": "v2.1.0" + }, + "aws/vpc": { + "path": "modules/aws/vpc", + "latestTag": "aws/vpc/v1.0.0", + "latestTagVersion": "v1.0.0" } } } diff --git a/__tests__/templating.test.ts b/__tests__/templating.test.ts deleted file mode 100644 index 82c62d4e..00000000 --- a/__tests__/templating.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { render } from '../src/templating'; - -describe('templating', () => { - it('should replace a single placeholder', () => { - const template = 'Hello, {{name}}!'; - const variables = { name: 'World' }; - const result = render(template, variables); - expect(result).toBe('Hello, World!'); - }); - - it('should replace multiple placeholders', () => { - const template = '{{greeting}}, {{name}}!'; - const variables = { greeting: 'Hi', name: 'There' }; - const result = render(template, variables); - expect(result).toBe('Hi, There!'); - }); - - it('should handle templates with no placeholders', () => { - const template = 'Just a plain string.'; - const variables = { name: 'World' }; - const result = render(template, variables); - expect(result).toBe('Just a plain string.'); - }); - - it('should handle empty string values', () => { - const template = 'A{{key}}B'; - const variables = { key: '' }; - const result = render(template, variables); - expect(result).toBe('AB'); - }); - - it('should leave unmapped placeholders untouched', () => { - const template = 'Hello, {{name}} and {{unmapped}}!'; - const variables = { name: 'World' }; - const result = render(template, variables); - expect(result).toBe('Hello, World and {{unmapped}}!'); - }); -}); diff --git a/__tests__/utils/metadata.test.ts b/__tests__/utils/metadata.test.ts index 53e9c977..e4847a99 100644 --- a/__tests__/utils/metadata.test.ts +++ b/__tests__/utils/metadata.test.ts @@ -1,6 +1,6 @@ import { ACTION_INPUTS, createConfigFromInputs } from '@/utils/metadata'; import type { ActionInputMetadata } from '@/types'; -import { getBooleanInput, getInput } from '@actions/core'; +import { getInput } from '@actions/core'; import { describe, expect, it, vi } from 'vitest'; describe('utils/metadata', () => { @@ -15,6 +15,7 @@ describe('utils/metadata', () => { 'delete-legacy-tags', 'disable-wiki', 'wiki-sidebar-changelog-max', + 'wiki-usage-template', 'disable-branding', 'module-path-ignore', 'module-change-exclude-patterns', @@ -167,59 +168,5 @@ describe('utils/metadata', () => { `Failed to process input 'major-keywords': ${String(errorObject)}`, ); }); - - it('should process all input types correctly', () => { - // Mock the GitHub Actions core functions - vi.mocked(getInput).mockImplementation((name) => { - const mockValues: Record = { - 'major-keywords': 'breaking,major', - 'minor-keywords': 'feat,feature', - 'patch-keywords': 'fix,chore', - 'default-first-tag': 'v1.0.0', - 'terraform-docs-version': 'v0.20.0', - 'wiki-sidebar-changelog-max': '5', - 'module-path-ignore': '', - 'module-change-exclude-patterns': '*.md,tests/**', - 'module-asset-exclude-patterns': '*.md,tests/**', - github_token: 'fake-token', - 'tag-directory-separator': '/', - 'use-ssh-source-format': 'false', - }; - return mockValues[name] || ''; - }); - - vi.mocked(getBooleanInput).mockImplementation((name) => { - const mockBooleans: Record = { - 'delete-legacy-tags': true, - 'disable-wiki': false, - 'disable-branding': false, - 'use-ssh-source-format': false, - 'use-version-prefix': true, - }; - return mockBooleans[name] || false; - }); - - const config = createConfigFromInputs(); - - // Verify all config properties are set - expect(config).toEqual({ - majorKeywords: ['breaking', 'major'], - minorKeywords: ['feat', 'feature'], - patchKeywords: ['fix', 'chore'], - defaultFirstTag: 'v1.0.0', - terraformDocsVersion: 'v0.20.0', - deleteLegacyTags: true, - disableWiki: false, - wikiSidebarChangelogMax: 5, - disableBranding: false, - modulePathIgnore: [], - moduleChangeExcludePatterns: ['*.md', 'tests/**'], - moduleAssetExcludePatterns: ['*.md', 'tests/**'], - useSSHSourceFormat: false, - githubToken: 'fake-token', - tagDirectorySeparator: '/', - useVersionPrefix: true, - }); - }); }); }); diff --git a/__tests__/utils/string.test.ts b/__tests__/utils/string.test.ts index 5c681789..6d95e89a 100644 --- a/__tests__/utils/string.test.ts +++ b/__tests__/utils/string.test.ts @@ -1,4 +1,4 @@ -import { removeLeadingCharacters, removeTrailingCharacters } from '@/utils/string'; +import { removeLeadingCharacters, removeTrailingCharacters, renderTemplate } from '@/utils/string'; import { describe, expect, it } from 'vitest'; describe('utils/string', () => { @@ -91,4 +91,90 @@ describe('utils/string', () => { expect(removeTrailingCharacters('example-_./', ['/', '.', '_', '-'])).toBe('example'); }); }); + + describe('renderTemplate', () => { + it('should replace a single placeholder', () => { + const template = 'Hello, {{name}}!'; + const variables = { name: 'World' }; + const result = renderTemplate(template, variables); + expect(result).toBe('Hello, World!'); + }); + + it('should replace multiple placeholders', () => { + const template = '{{greeting}}, {{name}}!'; + const variables = { greeting: 'Hi', name: 'There' }; + const result = renderTemplate(template, variables); + expect(result).toBe('Hi, There!'); + }); + + it('should handle templates with no placeholders', () => { + const template = 'Just a plain string.'; + const variables = { name: 'World' }; + const result = renderTemplate(template, variables); + expect(result).toBe('Just a plain string.'); + }); + + it('should handle empty string values', () => { + const template = 'A{{key}}B'; + const variables = { key: '' }; + const result = renderTemplate(template, variables); + expect(result).toBe('AB'); + }); + + it('should leave unmapped placeholders untouched', () => { + const template = 'Hello, {{name}} and {{unmapped}}!'; + const variables = { name: 'World' }; + const result = renderTemplate(template, variables); + expect(result).toBe('Hello, World and {{unmapped}}!'); + }); + + it('should handle complex templates with multiple variables', () => { + const template = 'Module: {{module}}, Version: {{version}}, Author: {{author}}'; + const variables = { module: 'vpc-endpoint', version: '1.0.0', author: 'TechPivot' }; + const result = renderTemplate(template, variables); + expect(result).toBe('Module: vpc-endpoint, Version: 1.0.0, Author: TechPivot'); + }); + + it('should handle numeric values as strings', () => { + const template = 'Port: {{port}}, Count: {{count}}'; + const variables = { port: '8080', count: '3' }; + const result = renderTemplate(template, variables); + expect(result).toBe('Port: 8080, Count: 3'); + }); + + it('should handle special characters in values', () => { + const template = 'Path: {{path}}, Command: {{cmd}}'; + const variables = { path: '/opt/bin/terraform', cmd: 'terraform init -backend=false' }; + const result = renderTemplate(template, variables); + expect(result).toBe('Path: /opt/bin/terraform, Command: terraform init -backend=false'); + }); + + it('should handle empty template', () => { + const template = ''; + const variables = { name: 'World' }; + const result = renderTemplate(template, variables); + expect(result).toBe(''); + }); + + it('should handle empty variables object', () => { + const template = 'Hello, {{name}}!'; + const variables = {}; + const result = renderTemplate(template, variables); + expect(result).toBe('Hello, {{name}}!'); + }); + + it('should handle placeholders with different casing', () => { + const template = 'Hello, {{Name}} and {{NAME}}!'; + const variables = { Name: 'World', NAME: 'UNIVERSE' }; + const result = renderTemplate(template, variables); + expect(result).toBe('Hello, World and UNIVERSE!'); + }); + + it('should handle placeholders with numbers', () => { + const template = 'Item {{item1}} and {{item2}}'; + const variables = { item1: 'first', item2: 'second' }; + const result = renderTemplate(template, variables); + expect(result).toBe('Item first and second'); + }); + }); }); diff --git a/__tests__/wiki.test.ts b/__tests__/wiki.test.ts index 0c587380..4f5bc5f6 100644 --- a/__tests__/wiki.test.ts +++ b/__tests__/wiki.test.ts @@ -305,7 +305,6 @@ describe('wiki', async () => { basename(file) !== '_Footer.md' ) { const content = readFileSync(file, 'utf8'); - const moduleName = basename(file, '.md'); expect(content).toContain(`# Usage\n\nModule: ${terraformModule.name}, Missing: {{missing_variable}}`); } } @@ -314,7 +313,7 @@ describe('wiki', async () => { it('should handle all variables in the custom usage template', async () => { const customUsage = 'Name: {{module_name}}, Tag: {{latest_tag}}, Version: {{latest_tag_version_number}}, Source: {{module_source}}, TFName: {{module_name_terraform}}'; - config.set({ wikiUsageTemplate: customUsage }); + config.set({ useSSHSourceFormat: true, wikiUsageTemplate: customUsage }); const files = await generateWikiFiles(terraformModules); for (const file of files) { if ( @@ -328,7 +327,7 @@ describe('wiki', async () => { // vpc-endpoint is the only one with a tag in the test setup if (moduleName === 'vpc‒endpoint') { expect(content).toContain( - 'Name: vpc-endpoint, Tag: vpc-endpoint/v1.0.0, Version: 1.0.0, Source: https://github.com/techpivot/terraform-module-releaser.git, TFName: vpc_endpoint', + 'Name: vpc-endpoint, Tag: vpc-endpoint/v1.0.0, Version: 1.0.0, Source: git::ssh://git@github.com/techpivot/terraform-module-releaser.git, TFName: vpc_endpoint', ); } } diff --git a/action.yml b/action.yml index bcf19c22..08d95155 100644 --- a/action.yml +++ b/action.yml @@ -51,6 +51,19 @@ inputs: Adjust this value to control the visibility of changelog entries in the module sidebar. required: true default: "5" + wiki-usage-template: + description: A raw, multi-line string to override the default 'Usage' section in the generated wiki. If not provided, a default usage block will be generated. + required: false + default: | + To use this module in your Terraform, refer to the below module example: + + ```hcl + module "{{module_name_terraform}}" { + source = "{{module_source}}?ref={{latest_tag}}" + + # See inputs below for additional required parameters + } + ``` disable-branding: description: > Flag to control whether the small branding link should be disabled or not in the @@ -102,19 +115,6 @@ inputs: If enabled, all links to source code in generated Wiki documentation will use SSH format instead of HTTPS format. required: true default: "false" - wiki-usage-template: - description: A raw, multi-line string to override the default 'Usage' section in the generated wiki. If not provided, a default usage block will be generated. - required: false - default: | - To use this module in your Terraform, refer to the below module example: - - ```hcl - module "{{module_name_terraform}}" { - source = "git::{{module_source}}?ref={{latest_tag}}" - - # See inputs below for additional required parameters - } - ``` github_token: description: > Required for retrieving pull request metadata, tags, releases, updating PR comments, wiki, and creating diff --git a/scripts/action.yml b/scripts/action.yml new file mode 100644 index 00000000..08d95155 --- /dev/null +++ b/scripts/action.yml @@ -0,0 +1,163 @@ +name: Terraform Module Releaser +description: Automate versioning, releases, and documentation for Terraform modules in GitHub monorepos. +author: TechPivot +branding: + icon: package + color: purple + +inputs: + major-keywords: + description: Keywords in commit messages that indicate a major release. + required: true + default: major change,breaking change + minor-keywords: + description: Keywords in commit messages that indicate a minor release. + required: true + default: feat,feature + patch-keywords: + description: > + Keywords in commit messages that indicate a patch release. By default, everything will be a patch + release if major or minor keywords are not found. + required: true + default: fix,chore,docs + default-first-tag: + description: Specifies the default tag version. (Should be in format v#.#.#) + required: true + default: v1.0.0 + terraform-docs-version: + description: > + Specifies the terraform-docs version used to generate documentation for the wiki. + + See: https://github.com/terraform-docs/terraform-docs/releases + required: true + default: v0.20.0 + delete-legacy-tags: + description: > + Specifies a boolean that determines whether tags from Terraform modules that have been deleted + should be automatically removed. By default this is true as the purpose of the repository is to keep + releases/tags clean. When removing a module, this will ensure the tags/releases are automatically + cleaned. + required: true + default: "true" + disable-wiki: + description: > + Whether to disable wiki generation for Terraform modules. + By default, this is set to false. Set to true to prevent wiki documentation from being generated. + required: true + default: "false" + wiki-sidebar-changelog-max: + description: > + An integer that specifies how many changelog entries are displayed in the sidebar per module. + Adjust this value to control the visibility of changelog entries in the module sidebar. + required: true + default: "5" + wiki-usage-template: + description: A raw, multi-line string to override the default 'Usage' section in the generated wiki. If not provided, a default usage block will be generated. + required: false + default: | + To use this module in your Terraform, refer to the below module example: + + ```hcl + module "{{module_name_terraform}}" { + source = "{{module_source}}?ref={{latest_tag}}" + + # See inputs below for additional required parameters + } + ``` + disable-branding: + description: > + Flag to control whether the small branding link should be disabled or not in the + pull request (PR) comments. When branding is enabled, a link to the action's + repository is added at the bottom of comments. Setting this flag to "true" + will remove that link. Useful for cleaner PR comments in enterprise environments + or where third-party branding is undesirable. + required: true + default: "false" + module-path-ignore: + description: > + A comma-separated list of module paths to completely ignore during processing. Paths matching these patterns will + not be considered for versioning, releases, or documentation generation. These patterns follow glob syntax and are + relative to the repository root. Use this to exclude example modules, test modules, or other paths that should not + be treated as releasable modules. + + This is a top-level filter: if a module matches any of these patterns, it is completely excluded from all release, versioning, and documentation logic. No releases will ever happen for a module that matches this filter. + + The minimatch syntax is used for pattern matching. Patterns are relative to the workspace directory (no leading + slash). + + NOTE: To match both a directory and its contents, use separate patterns (e.g., "dir,dir/**"). + required: false + default: "" + module-change-exclude-patterns: + description: > + A comma-separated list of file patterns to exclude from triggering version changes in Terraform modules. + These patterns follow glob syntax and are relative to each Terraform module directory, not the repository root. + Files matching these patterns will be tracked in the module but changes to them won't trigger a new version. + Uses matchBase: true for pattern matching, so patterns like "*.md" will match files in any subdirectory. + + This option allows you to release a module but control which files inside a matched Terraform module should not force a bump of the module version. For example, you may want to exclude documentation or test files from triggering a release, but still include them in the module asset bundle. + + WARNING: Avoid excluding '*.tf' files, as they are essential for module functionality and versioning. + required: true + default: ".gitignore,*.md,*.tftest.hcl,tests/**" + module-asset-exclude-patterns: + description: > + A comma-separated list of file patterns to exclude when bundling a Terraform module for release assets. + These patterns follow glob syntax and are relative to each Terraform module directory, not the repository root. + Files matching these patterns will be excluded from the bundled release archives. Uses matchBase: true for + pattern matching, similar to module-change-exclude-patterns. + + These patterns only affect what's included in release assets and do not impact versioning decisions. + required: true + default: ".gitignore,*.md,*.tftest.hcl,tests/**" + use-ssh-source-format: + description: > + If enabled, all links to source code in generated Wiki documentation will use SSH format instead of HTTPS format. + required: true + default: "false" + github_token: + description: > + Required for retrieving pull request metadata, tags, releases, updating PR comments, wiki, and creating + tags/releases. Automatically injected for convenience; no need to provide a custom token unless you have + specific requirements. + required: true + default: ${{ github.token }} + tag-directory-separator: + description: > + Character used to separate directory path components in Git tags. This separator is used to convert + module directory paths into tag names (e.g., 'modules/aws/s3-bucket' becomes 'modules-aws-s3-bucket-v1.0.0' + when using '-'). Must be a single character from: /, -, _, or . + + Examples with different separators: + - "/" (default): modules/aws/s3-bucket/v1.0.0 + - "-": modules-aws-s3-bucket-v1.0.0 + - "_": modules_aws_s3_bucket_v1.0.0 + - ".": modules.aws.s3.bucket.v1.0.0 + required: true + default: / + use-version-prefix: + description: > + Whether to include the 'v' prefix on version tags (e.g., v1.2.3 vs 1.2.3). When enabled, all new version + tags will include the 'v' prefix. For initial releases, this setting takes precedence over any 'v' prefix + specified in the default-first-tag - if use-version-prefix is false and default-first-tag contains 'v', + the 'v' will be automatically removed to ensure consistency. + required: true + default: "true" + +outputs: + changed-module-names: + description: JSON array of module names that were changed in the current pull request + changed-module-paths: + description: JSON array of file system paths to the modules that were changed + changed-modules-map: + description: JSON object mapping module names to their change details including current tag, next tag, and release type + all-module-names: + description: JSON array of all module names found in the repository + all-module-paths: + description: JSON array of file system paths to all modules in the repository + all-modules-map: + description: JSON object mapping all module names to their details including path, latest tag, and latest tag version + +runs: + using: node20 + main: dist/index.js diff --git a/src/templating.ts b/src/templating.ts deleted file mode 100644 index da3eec67..00000000 --- a/src/templating.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Renders a template string by replacing placeholders with provided values. - * - * @param template The template string containing placeholders in the format `{{key}}`. - * @param variables An object where keys correspond to placeholder names and values are their replacements. - * @returns The rendered string with placeholders replaced. - */ -export const render = (template: string, variables: Record): string => { - return template.replace(/\{\{(\w+)\}\}/g, (placeholder, key) => { - return key in variables ? variables[key] : placeholder; - }); -}; diff --git a/src/types/config.types.ts b/src/types/config.types.ts index 8961f8c9..06332070 100644 --- a/src/types/config.types.ts +++ b/src/types/config.types.ts @@ -53,6 +53,17 @@ export interface Config { */ wikiSidebarChangelogMax: number; + /** + * A raw, multi-line string to override the default 'Usage' section in the generated wiki. + * If not provided, a default usage block will be generated. Supports template variables like: + * - {{module_name}}: The name of the module + * - {{latest_tag}}: The latest git tag for the module + * - {{latest_tag_version_number}}: The version number from the latest tag + * - {{module_source}}: The source URL for the module + * - {{module_name_terraform}}: The module name formatted for Terraform usage (alphanumeric and underscores only) + */ + wikiUsageTemplate: string; + /** * Flag to control whether the small branding link should be disabled or not in the * pull request (PR) comments. When branding is enabled, a link to the action's diff --git a/src/utils/metadata.ts b/src/utils/metadata.ts index aca1fb66..07c32530 100644 --- a/src/utils/metadata.ts +++ b/src/utils/metadata.ts @@ -49,6 +49,7 @@ export const ACTION_INPUTS: Record = { 'delete-legacy-tags': requiredBoolean('deleteLegacyTags'), 'disable-wiki': requiredBoolean('disableWiki'), 'wiki-sidebar-changelog-max': requiredNumber('wikiSidebarChangelogMax'), + 'wiki-usage-template': requiredString('wikiUsageTemplate'), 'disable-branding': requiredBoolean('disableBranding'), 'module-path-ignore': optionalArray('modulePathIgnore'), 'module-change-exclude-patterns': optionalArray('moduleChangeExcludePatterns'), diff --git a/src/utils/string.ts b/src/utils/string.ts index 6a83bb1d..6b0a7546 100644 --- a/src/utils/string.ts +++ b/src/utils/string.ts @@ -55,3 +55,24 @@ export function removeLeadingCharacters(input: string, charactersToRemove: strin return input.slice(startIndex); } + +/** + * Renders a template string by replacing placeholders with provided values. + * + * @param template The template string containing placeholders in the format `{{key}}`. + * @param variables An object where keys correspond to placeholder names and values are their replacements. + * @returns The rendered string with placeholders replaced. + * + * @example + * // Returns "Hello, World!" + * renderTemplate("Hello, {{name}}!", { name: "World" }) + * + * @example + * // Returns "Hi, There!" + * renderTemplate("{{greeting}}, {{name}}!", { greeting: "Hi", name: "There" }) + */ +export function renderTemplate(template: string, variables: Record): string { + return template.replace(/\{\{(\w+)\}\}/g, (placeholder, key) => { + return key in variables ? variables[key] : placeholder; + }); +} diff --git a/src/wiki.ts b/src/wiki.ts index 49ba5f4c..4d0606e0 100644 --- a/src/wiki.ts +++ b/src/wiki.ts @@ -7,7 +7,7 @@ import { join, resolve } from 'node:path'; import { getTerraformModuleFullReleaseChangelog } from '@/changelog'; import { config } from '@/config'; import { context } from '@/context'; -import { render } from '@/templating'; +import { renderTemplate } from '@/utils/string'; import { generateTerraformDocs } from '@/terraform-docs'; import type { TerraformModule } from '@/terraform-module'; import type { ExecSyncError, WikiStatusResult } from '@/types'; @@ -239,24 +239,24 @@ export function getWikiLink(moduleName: string, relative = true): string { /** * Formats the module source URL based on configuration settings. * - * Converts repository URLs to the appropriate format for module sourcing: - * - SSH format: ssh://git@hostname/path.git - * - HTTPS format: https://hostname/path.git + * Converts repository URLs to the appropriate format for Terraform module sourcing: + * - SSH format: git::ssh://git@hostname/path.git + * - HTTPS format: git::https://hostname/path.git * * @param repoUrl - The repository URL (must be a valid HTTPS URL) * @param useSSH - Whether to use SSH format instead of HTTPS - * @returns The formatted source URL for the module + * @returns The formatted source URL for the module with git:: prefix * @throws {TypeError} When repoUrl is not a valid URL that can be parsed * * @example * ```typescript * // HTTPS format * getModuleSource('https://github.com/owner/repo', false) - * // Returns: 'https://github.com/owner/repo.git' + * // Returns: 'git::https://github.com/owner/repo.git' * * // SSH format * getModuleSource('https://github.techpivot.com/owner/repo', true) - * // Returns: 'ssh://git@github.techpivot.com/owner/repo.git' + * // Returns: 'git::ssh://git@github.techpivot.com/owner/repo.git' * ``` */ function getModuleSource(repoUrl: string, useSSH: boolean): string { @@ -265,13 +265,14 @@ function getModuleSource(repoUrl: string, useSSH: boolean): string { const hostname = url.hostname; const pathname = url.pathname; - // Convert HTTPS URL to SSH format + // Convert HTTPS URL to SSH format with git:: prefix // From: https://github.techpivot.com/owner/repo - // To: ssh://git@github.techpivot.com/owner/repo.git - return `ssh://git@${hostname}${pathname}.git`; + // To: git::ssh://git@github.techpivot.com/owner/repo.git + return `git::ssh://git@${hostname}${pathname}.git`; } - return `${repoUrl}.git`; + // Return HTTPS format with git:: prefix + return `git::${repoUrl}.git`; } /** @@ -306,10 +307,10 @@ async function generateWikiTerraformModule(terraformModule: TerraformModule): Pr const tfDocs = await generateTerraformDocs(terraformModule); const moduleSource = getModuleSource(context.repoUrl, config.useSSHSourceFormat); - const usage = render(config.wikiUsageTemplate, { + const usage = renderTemplate(config.wikiUsageTemplate, { module_name: terraformModule.name, - latest_tag: terraformModule.getLatestTag(), - latest_tag_version_number: terraformModule.getLatestTagVersionNumber(), + latest_tag: terraformModule.getLatestTag() ?? '', + latest_tag_version_number: terraformModule.getLatestTagVersionNumber() ?? '', module_source: moduleSource, module_name_terraform: terraformModule.name.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase(), }); diff --git a/tf-modules/vpc-endpoint/main.tf b/tf-modules/vpc-endpoint/main.tf index d9b05a90..d39107a3 100644 --- a/tf-modules/vpc-endpoint/main.tf +++ b/tf-modules/vpc-endpoint/main.tf @@ -1,4 +1,5 @@ # Noop +# Test locals { endpoints = { for k, v in var.endpoints : k => v } From cc5eec22ad312d6a90a441c016ea2681e8eddee9 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Thu, 21 Aug 2025 05:39:49 +0000 Subject: [PATCH 28/29] fix: update changelog script import path in release workflow refactor: remove redundant release content structure validation from tests chore: update biome configuration to include all scripts chore: add new changelog script and remove deprecated action.yml test: add parse-modules-test script for local testing of parseTerraformModules function feat: implement changelog generation with GitHub API integration delete: remove unused development script for parsing modules --- .devcontainer/devcontainer.json | 1 + .github/workflows/lint.yml | 2 +- .github/workflows/release-start.yml | 2 +- README.md | 6 - __tests__/releases.test.ts | 20 +-- action.yml | 2 +- biome.json | 11 +- package.json | 14 +- scripts/action.yml | 163 ------------------ {.github/scripts => scripts}/changelog.js | 21 +-- ...parse-modules.ts => parse-modules-test.ts} | 3 + 11 files changed, 40 insertions(+), 205 deletions(-) delete mode 100644 scripts/action.yml rename {.github/scripts => scripts}/changelog.js (97%) rename scripts/{dev-parse-modules.ts => parse-modules-test.ts} (95%) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 9bf68b61..6dbb8368 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -51,6 +51,7 @@ "GITHUB_TOKEN": "${localEnv:GITHUB_TOKEN}" }, "features": { + "ghcr.io/devcontainers/features/github-cli:1": {}, "ghcr.io/devcontainers-community/npm-features/prettier": {} } } diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 4cf426c9..4bdf632c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -42,7 +42,7 @@ jobs: - name: Lint Codebase id: super-linter - uses: super-linter/super-linter@12150456a73e248bdc94d0794898f94e23127c88 # v7.4.0 + uses: super-linter/super-linter@5119dcd8011e92182ce8219d9e9efc82f16fddb6 # v8.0.0 env: DEFAULT_BRANCH: main GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-start.yml b/.github/workflows/release-start.yml index 1c498dc9..b455bf2a 100644 --- a/.github/workflows/release-start.yml +++ b/.github/workflows/release-start.yml @@ -66,7 +66,7 @@ jobs: with: result-encoding: json script: | - const { generateChangelog } = await import('${{ github.workspace }}/.github/scripts/changelog.js'); + const { generateChangelog } = await import('${{ github.workspace }}/scripts/changelog.js'); try { const changelog = await generateChangelog("${{ env.VERSION }}"); diff --git a/README.md b/README.md index 66cf6eef..3221f8af 100644 --- a/README.md +++ b/README.md @@ -207,14 +207,12 @@ configuring the following optional input parameters as needed. - **`module-path-ignore`**: Completely ignores specified module paths. Any module whose path matches any pattern in this list will not be processed at all by the action. This is useful for: - - Excluding example modules (e.g., `**/examples/**`) - Skipping test modules (e.g., `**/test/**`) - Ignoring documentation-focused modules (e.g., `**/docs/**`) - Excluding entire directories or paths that contain Terraform files but shouldn't be versioned as modules **Important pattern matching notes:** - - Patterns are relative to the workspace directory (no leading slash) - A pattern like `dir/**` will match files and directories INSIDE `dir` but NOT the `dir` directory itself - To match both a directory and its contents, you need both patterns: `dir,dir/**` @@ -230,7 +228,6 @@ configuring the following optional input parameters as needed. a new module release. **Key details:** - - Patterns are relative to each **module directory**, not the workspace root - Uses `matchBase: true` for pattern matching, so `*.md` will match any Markdown file in any subdirectory - Applied only after a module is identified but before determining if it needs a version change @@ -238,7 +235,6 @@ configuring the following optional input parameters as needed. - Use this for files that should be part of the module but don't affect its functionality **Common use cases:** - - Documentation files (`*.md`, `docs/**`) - Test files (`tests/**`, `*.tftest.hcl`) - Examples (`examples/**`) @@ -256,7 +252,6 @@ configuring the following optional input parameters as needed. are _excluded_ from the asset. This helps reduce the asset size by omitting test files, examples, documentation, etc. **Key details:** - - Patterns are relative to each **module directory**, not the workspace root - Uses `matchBase: true` for pattern matching (same as `module-change-exclude-patterns`) - Applied during the bundle/archive creation phase for GitHub releases @@ -264,7 +259,6 @@ configuring the following optional input parameters as needed. - These patterns do NOT affect versioning decisions - only the contents of release assets **Common use cases:** - - Test directories and files (`tests/**`, `*.tftest.hcl`) - Documentation that's not needed for module functionality (`*.md`) - Development-specific files (`.gitignore`, `CHANGELOG.md`) diff --git a/__tests__/releases.test.ts b/__tests__/releases.test.ts index 27bd6a34..2f3ace13 100644 --- a/__tests__/releases.test.ts +++ b/__tests__/releases.test.ts @@ -96,26 +96,12 @@ describe('releases', () => { expect(versions).toEqual(sortedVersions); }); - it('should validate release content structure', () => { - for (const release of releases) { - // Basic structure checks - expect(release).toHaveProperty('id'); - expect(release).toHaveProperty('title'); - expect(release).toHaveProperty('body'); - - // Title format check (should at least contain v1.1.1) - expect(release.title).toMatch(/v\d+\.\d+\.\d+/); - - // Body content checks - expect(typeof release.body).toBe('string'); - expect(release.body.length).toBeGreaterThan(0); - } - }); - it('should verify specific release contents', () => { - // Find v1.3.0 release + // Find v1.3.0 release (This is specific for this repo - which is fine as we are just testing release object parsing) const v130Release = releases.find((r) => r.title === 'v1.3.0'); expect(v130Release).toBeDefined(); + expect(v130Release?.tagName).toBe('v1.3.0'); + expect(v130Release?.title).toBe('v1.3.0'); expect(v130Release?.id).toBe(182147836); expect(v130Release?.body).toContain('Enhanced Wiki Generation'); expect(v130Release?.body).toContain('Asset & Exclude Pattern Filtering'); diff --git a/action.yml b/action.yml index 08d95155..0f5f0d09 100644 --- a/action.yml +++ b/action.yml @@ -127,7 +127,7 @@ inputs: Character used to separate directory path components in Git tags. This separator is used to convert module directory paths into tag names (e.g., 'modules/aws/s3-bucket' becomes 'modules-aws-s3-bucket-v1.0.0' when using '-'). Must be a single character from: /, -, _, or . - + Examples with different separators: - "/" (default): modules/aws/s3-bucket/v1.0.0 - "-": modules-aws-s3-bucket-v1.0.0 diff --git a/biome.json b/biome.json index 7ed801a8..a0f6bd5c 100644 --- a/biome.json +++ b/biome.json @@ -9,7 +9,16 @@ "enabled": false }, "files": { - "includes": ["*.md", "*.ts", "__mocks__/**/*.ts", "__tests__/**/*.ts", "src/**/*.ts", "scripts/**/*.ts"] + "includes": [ + "*.md", + "*.ts", + "*.json", + "__mocks__/**/*.ts", + "__tests__/**/*.ts", + ".devcontainer/*.json", + "src/**/*.ts", + "scripts/**/*" + ] }, "formatter": { "enabled": true, diff --git a/package.json b/package.json index c1a52f13..ad541b92 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,13 @@ "bugs": { "url": "https://github.com/techpivot/terraform-module-releaser/issues" }, - "keywords": ["terraform", "module", "releaser", "github-action", "monorepo"], + "keywords": [ + "terraform", + "module", + "releaser", + "github-action", + "monorepo" + ], "license": "MIT", "exports": { ".": "./dist/index.js" @@ -36,6 +42,7 @@ "bundle": "npm run check:fix && npm run package", "check": "biome check ./src", "check:fix": "biome check --write --unsafe .", + "prettier": "npx prettier -w *.md", "dev:parse-modules": "tsx scripts/dev-parse-modules.ts", "textlint": "textlint -c .github/linters/.textlintrc **/*.md", "textlint:fix": "textlint -c .github/linters/.textlintrc --fix **/*.md", @@ -43,9 +50,10 @@ "package": "ncc build src/index.ts --source-map -o dist", "test": "vitest run --coverage", "test:watch": "vitest", + "test:parse-modules": "tsx scripts/parse-modules-test.ts", + "changelog": "node scripts/changelog.js 10.0.0", "coverage": "make-coverage-badge --output-path ./assets/coverage-badge.svg", - "terraform-docs-version": "gh api repos/terraform-docs/terraform-docs/releases/latest --jq '.tag_name'", - "changelog:test": "node .github/scripts/changelog.js 10.0.0" + "terraform-docs-version": "gh api repos/terraform-docs/terraform-docs/releases/latest --jq '.tag_name'" }, "dependencies": { "@actions/core": "^1.11.1", diff --git a/scripts/action.yml b/scripts/action.yml deleted file mode 100644 index 08d95155..00000000 --- a/scripts/action.yml +++ /dev/null @@ -1,163 +0,0 @@ -name: Terraform Module Releaser -description: Automate versioning, releases, and documentation for Terraform modules in GitHub monorepos. -author: TechPivot -branding: - icon: package - color: purple - -inputs: - major-keywords: - description: Keywords in commit messages that indicate a major release. - required: true - default: major change,breaking change - minor-keywords: - description: Keywords in commit messages that indicate a minor release. - required: true - default: feat,feature - patch-keywords: - description: > - Keywords in commit messages that indicate a patch release. By default, everything will be a patch - release if major or minor keywords are not found. - required: true - default: fix,chore,docs - default-first-tag: - description: Specifies the default tag version. (Should be in format v#.#.#) - required: true - default: v1.0.0 - terraform-docs-version: - description: > - Specifies the terraform-docs version used to generate documentation for the wiki. - - See: https://github.com/terraform-docs/terraform-docs/releases - required: true - default: v0.20.0 - delete-legacy-tags: - description: > - Specifies a boolean that determines whether tags from Terraform modules that have been deleted - should be automatically removed. By default this is true as the purpose of the repository is to keep - releases/tags clean. When removing a module, this will ensure the tags/releases are automatically - cleaned. - required: true - default: "true" - disable-wiki: - description: > - Whether to disable wiki generation for Terraform modules. - By default, this is set to false. Set to true to prevent wiki documentation from being generated. - required: true - default: "false" - wiki-sidebar-changelog-max: - description: > - An integer that specifies how many changelog entries are displayed in the sidebar per module. - Adjust this value to control the visibility of changelog entries in the module sidebar. - required: true - default: "5" - wiki-usage-template: - description: A raw, multi-line string to override the default 'Usage' section in the generated wiki. If not provided, a default usage block will be generated. - required: false - default: | - To use this module in your Terraform, refer to the below module example: - - ```hcl - module "{{module_name_terraform}}" { - source = "{{module_source}}?ref={{latest_tag}}" - - # See inputs below for additional required parameters - } - ``` - disable-branding: - description: > - Flag to control whether the small branding link should be disabled or not in the - pull request (PR) comments. When branding is enabled, a link to the action's - repository is added at the bottom of comments. Setting this flag to "true" - will remove that link. Useful for cleaner PR comments in enterprise environments - or where third-party branding is undesirable. - required: true - default: "false" - module-path-ignore: - description: > - A comma-separated list of module paths to completely ignore during processing. Paths matching these patterns will - not be considered for versioning, releases, or documentation generation. These patterns follow glob syntax and are - relative to the repository root. Use this to exclude example modules, test modules, or other paths that should not - be treated as releasable modules. - - This is a top-level filter: if a module matches any of these patterns, it is completely excluded from all release, versioning, and documentation logic. No releases will ever happen for a module that matches this filter. - - The minimatch syntax is used for pattern matching. Patterns are relative to the workspace directory (no leading - slash). - - NOTE: To match both a directory and its contents, use separate patterns (e.g., "dir,dir/**"). - required: false - default: "" - module-change-exclude-patterns: - description: > - A comma-separated list of file patterns to exclude from triggering version changes in Terraform modules. - These patterns follow glob syntax and are relative to each Terraform module directory, not the repository root. - Files matching these patterns will be tracked in the module but changes to them won't trigger a new version. - Uses matchBase: true for pattern matching, so patterns like "*.md" will match files in any subdirectory. - - This option allows you to release a module but control which files inside a matched Terraform module should not force a bump of the module version. For example, you may want to exclude documentation or test files from triggering a release, but still include them in the module asset bundle. - - WARNING: Avoid excluding '*.tf' files, as they are essential for module functionality and versioning. - required: true - default: ".gitignore,*.md,*.tftest.hcl,tests/**" - module-asset-exclude-patterns: - description: > - A comma-separated list of file patterns to exclude when bundling a Terraform module for release assets. - These patterns follow glob syntax and are relative to each Terraform module directory, not the repository root. - Files matching these patterns will be excluded from the bundled release archives. Uses matchBase: true for - pattern matching, similar to module-change-exclude-patterns. - - These patterns only affect what's included in release assets and do not impact versioning decisions. - required: true - default: ".gitignore,*.md,*.tftest.hcl,tests/**" - use-ssh-source-format: - description: > - If enabled, all links to source code in generated Wiki documentation will use SSH format instead of HTTPS format. - required: true - default: "false" - github_token: - description: > - Required for retrieving pull request metadata, tags, releases, updating PR comments, wiki, and creating - tags/releases. Automatically injected for convenience; no need to provide a custom token unless you have - specific requirements. - required: true - default: ${{ github.token }} - tag-directory-separator: - description: > - Character used to separate directory path components in Git tags. This separator is used to convert - module directory paths into tag names (e.g., 'modules/aws/s3-bucket' becomes 'modules-aws-s3-bucket-v1.0.0' - when using '-'). Must be a single character from: /, -, _, or . - - Examples with different separators: - - "/" (default): modules/aws/s3-bucket/v1.0.0 - - "-": modules-aws-s3-bucket-v1.0.0 - - "_": modules_aws_s3_bucket_v1.0.0 - - ".": modules.aws.s3.bucket.v1.0.0 - required: true - default: / - use-version-prefix: - description: > - Whether to include the 'v' prefix on version tags (e.g., v1.2.3 vs 1.2.3). When enabled, all new version - tags will include the 'v' prefix. For initial releases, this setting takes precedence over any 'v' prefix - specified in the default-first-tag - if use-version-prefix is false and default-first-tag contains 'v', - the 'v' will be automatically removed to ensure consistency. - required: true - default: "true" - -outputs: - changed-module-names: - description: JSON array of module names that were changed in the current pull request - changed-module-paths: - description: JSON array of file system paths to the modules that were changed - changed-modules-map: - description: JSON object mapping module names to their change details including current tag, next tag, and release type - all-module-names: - description: JSON array of all module names found in the repository - all-module-paths: - description: JSON array of file system paths to all modules in the repository - all-modules-map: - description: JSON object mapping all module names to their details including path, latest tag, and latest tag version - -runs: - using: node20 - main: dist/index.js diff --git a/.github/scripts/changelog.js b/scripts/changelog.js similarity index 97% rename from .github/scripts/changelog.js rename to scripts/changelog.js index 5e0baed4..93d80a02 100644 --- a/.github/scripts/changelog.js +++ b/scripts/changelog.js @@ -8,8 +8,8 @@ * USAGE: * ------ * 1. Set up environment variables (see below) - * 2. Run via npm script: `npm run changelog:test -- "1.2.3"` - * 3. Or import and use: `import { generateChangelog } from './.github/scripts/changelog.js'` + * 2. Run via npm script: `npm run changelog -- "1.2.3"` + * 3. Or import and use: `import { generateChangelog } from './scripts/changelog.js'` * * TESTING LOCALLY: * ---------------- @@ -21,10 +21,10 @@ * export GITHUB_TOKEN="your_github_token_here" * * # Run the test script - * npm run changelog:test -- "1.2.3" + * npm run changelog" * - * # Or test with different versions - * npm run changelog:test -- "2.0.0" + * # Or test with different next version (Only used for display purposes) + * npm run changelog -- "2.0.0" * * Required Environment Variables: * ------------------------------ @@ -62,7 +62,7 @@ import https from 'node:https'; import OpenAI from 'openai'; const ENDPOINT = 'https://models.github.ai/inference'; -const MODEL = 'openai/gpt-4.1'; +const MODEL = 'openai/gpt-4.1'; // gpt-5 has max request size of 4000 tokens on free const PROMPT = ` You're the head of developer relations at a SaaS company. Write a concise, professional, and engaging changelog that prioritizes user-impacting changes and tells a story about the release. @@ -542,9 +542,6 @@ export { generateChangelog }; /** * CLI interface for testing the changelog generator locally. - * - * Usage: node .github/scripts/changelog.js [version] - * Example: node .github/scripts/changelog.js "1.2.3" */ async function main() { // Check if this script is being run directly (not imported) @@ -552,9 +549,9 @@ async function main() { const version = process.argv[2]; if (!version) { - console.error('❌ Error: Version argument is required'); - console.error('Usage: node .github/scripts/changelog.js [version]'); - console.error('Example: node .github/scripts/changelog.js "1.2.3"'); + console.error('❌ Error: Next version argument is required'); + console.error('Usage: node scripts/changelog.js [next-version]'); + console.error('Example: node scripts/changelog.js "1.2.3"'); process.exit(1); } diff --git a/scripts/dev-parse-modules.ts b/scripts/parse-modules-test.ts similarity index 95% rename from scripts/dev-parse-modules.ts rename to scripts/parse-modules-test.ts index f472f80e..d3153c00 100644 --- a/scripts/dev-parse-modules.ts +++ b/scripts/parse-modules-test.ts @@ -30,9 +30,12 @@ async function main() { process.env['INPUT_DELETE-LEGACY-TAGS'] = 'false'; process.env['INPUT_DISABLE-WIKI'] = 'true'; process.env['INPUT_WIKI-SIDEBAR-CHANGELOG-MAX'] = '5'; + process.env['INPUT_WIKI-USAGE-TEMPLATE'] = 'Wiki usage template'; process.env['INPUT_DISABLE-BRANDING'] = 'false'; process.env.INPUT_GITHUB_TOKEN = process.env.GITHUB_TOKEN; process.env['INPUT_USE-SSH-SOURCE-FORMAT'] = 'true'; + process.env['INPUT_TAG-DIRECTORY-SEPARATOR'] = '/'; + process.env['INPUT_USE-VERSION-PREFIX'] = 'true'; process.env['INPUT_MODULE-PATH-IGNORE'] = '**/examples/**'; process.env['INPUT_MODULE-CHANGE-EXCLUDE-PATTERNS'] = '.gitignore,*.md'; From f94d31ca40c59a318649140e39fc039a47dc3144 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Thu, 21 Aug 2025 06:23:58 +0000 Subject: [PATCH 29/29] fix: update documentation links and remove test comment in main.tf --- __tests__/utils/string.test.ts | 63 +++++++++++++++++++++++++++++++++ src/types/metadata.types.ts | 2 +- src/utils/constants.ts | 2 +- src/utils/string.ts | 14 ++++++-- src/wiki.ts | 4 +-- tf-modules/vpc-endpoint/main.tf | 1 - 6 files changed, 79 insertions(+), 7 deletions(-) diff --git a/__tests__/utils/string.test.ts b/__tests__/utils/string.test.ts index 6d95e89a..0834b36a 100644 --- a/__tests__/utils/string.test.ts +++ b/__tests__/utils/string.test.ts @@ -176,5 +176,68 @@ describe('utils/string', () => { const result = renderTemplate(template, variables); expect(result).toBe('Item first and second'); }); + + it('should handle undefined values by leaving placeholders unchanged', () => { + const template = 'Hello, {{name}} and {{greeting}}!'; + const variables = { name: 'World', greeting: undefined }; + const result = renderTemplate(template, variables); + expect(result).toBe('Hello, World and {{greeting}}!'); + }); + + it('should handle all undefined values', () => { + const template = '{{greeting}}, {{name}}!'; + const variables = { greeting: undefined, name: undefined }; + const result = renderTemplate(template, variables); + expect(result).toBe('{{greeting}}, {{name}}!'); + }); + + it('should handle mixed defined and undefined values', () => { + const template = 'Module: {{module}}, Version: {{version}}, Author: {{author}}'; + const variables = { module: 'vpc-endpoint', version: undefined, author: 'TechPivot' }; + const result = renderTemplate(template, variables); + expect(result).toBe('Module: vpc-endpoint, Version: {{version}}, Author: TechPivot'); + }); + + it('should handle empty string vs undefined distinction', () => { + const template = 'A{{key1}}B{{key2}}C'; + const variables = { key1: '', key2: undefined }; + const result = renderTemplate(template, variables); + expect(result).toBe('AB{{key2}}C'); + }); + + it('should handle undefined values in complex templates', () => { + const template = 'Path: {{path}}, Command: {{cmd}}, Options: {{opts}}'; + const variables = { path: '/opt/bin/terraform', cmd: undefined, opts: '--verbose' }; + const result = renderTemplate(template, variables); + expect(result).toBe('Path: /opt/bin/terraform, Command: {{cmd}}, Options: --verbose'); + }); + + it('should handle null values by leaving placeholders unchanged', () => { + const template = 'Hello, {{name}} and {{greeting}}!'; + const variables = { name: 'World', greeting: null }; + const result = renderTemplate(template, variables); + expect(result).toBe('Hello, World and {{greeting}}!'); + }); + + it('should handle all null values', () => { + const template = '{{greeting}}, {{name}}!'; + const variables = { greeting: null, name: null }; + const result = renderTemplate(template, variables); + expect(result).toBe('{{greeting}}, {{name}}!'); + }); + + it('should handle mixed null, undefined, and defined values', () => { + const template = 'Module: {{module}}, Version: {{version}}, Author: {{author}}, License: {{license}}'; + const variables = { module: 'vpc-endpoint', version: null, author: 'TechPivot', license: undefined }; + const result = renderTemplate(template, variables); + expect(result).toBe('Module: vpc-endpoint, Version: {{version}}, Author: TechPivot, License: {{license}}'); + }); + + it('should handle empty string vs null vs undefined distinction', () => { + const template = 'A{{key1}}B{{key2}}C{{key3}}D'; + const variables = { key1: '', key2: null, key3: undefined }; + const result = renderTemplate(template, variables); + expect(result).toBe('AB{{key2}}C{{key3}}D'); + }); }); }); diff --git a/src/types/metadata.types.ts b/src/types/metadata.types.ts index 4ebfff8e..69981950 100644 --- a/src/types/metadata.types.ts +++ b/src/types/metadata.types.ts @@ -14,7 +14,7 @@ import type { Config } from '@/types/config.types'; * comprehensive mapping of all action inputs, which then drives the automatic * config generation process. * - * @see {@link /workspaces/terraform-module-releaser/src/utils/metadata.ts} for usage + * @see utils/metadata.ts for usage * @see {@link https://docs.github.com/en/actions/reference/metadata-syntax-for-github-actions#inputs} GitHub Actions input reference */ export interface ActionInputMetadata { diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 7ed67192..b226f12c 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -10,7 +10,7 @@ * - `modules_aws_s3_bucket_v1.0.0` (using '_') * - `modules.aws.s3.bucket.v1.0.0` (using '.') * - * The default separator is '/' as defined in action.yml.dddd + * The default separator is '/' as defined in action.yml. */ export const VALID_TAG_DIRECTORY_SEPARATORS = ['-', '_', '/', '.']; diff --git a/src/utils/string.ts b/src/utils/string.ts index 6b0a7546..417a7c5b 100644 --- a/src/utils/string.ts +++ b/src/utils/string.ts @@ -61,6 +61,7 @@ export function removeLeadingCharacters(input: string, charactersToRemove: strin * * @param template The template string containing placeholders in the format `{{key}}`. * @param variables An object where keys correspond to placeholder names and values are their replacements. + * If a value is undefined or null, the placeholder will be left unchanged. * @returns The rendered string with placeholders replaced. * * @example @@ -70,9 +71,18 @@ export function removeLeadingCharacters(input: string, charactersToRemove: strin * @example * // Returns "Hi, There!" * renderTemplate("{{greeting}}, {{name}}!", { greeting: "Hi", name: "There" }) + * + * @example + * // Returns "Hello, {{name}}!" (undefined value leaves placeholder unchanged) + * renderTemplate("Hello, {{name}}!", { name: undefined }) + * + * @example + * // Returns "Hello, {{name}}!" (null value leaves placeholder unchanged) + * renderTemplate("Hello, {{name}}!", { name: null }) */ -export function renderTemplate(template: string, variables: Record): string { +export function renderTemplate(template: string, variables: Record): string { return template.replace(/\{\{(\w+)\}\}/g, (placeholder, key) => { - return key in variables ? variables[key] : placeholder; + const value = variables[key]; + return value !== undefined && value !== null ? value : placeholder; }); } diff --git a/src/wiki.ts b/src/wiki.ts index 4d0606e0..bcddd0ea 100644 --- a/src/wiki.ts +++ b/src/wiki.ts @@ -309,8 +309,8 @@ async function generateWikiTerraformModule(terraformModule: TerraformModule): Pr const usage = renderTemplate(config.wikiUsageTemplate, { module_name: terraformModule.name, - latest_tag: terraformModule.getLatestTag() ?? '', - latest_tag_version_number: terraformModule.getLatestTagVersionNumber() ?? '', + latest_tag: terraformModule.getLatestTag(), + latest_tag_version_number: terraformModule.getLatestTagVersionNumber(), module_source: moduleSource, module_name_terraform: terraformModule.name.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase(), }); diff --git a/tf-modules/vpc-endpoint/main.tf b/tf-modules/vpc-endpoint/main.tf index d39107a3..d9b05a90 100644 --- a/tf-modules/vpc-endpoint/main.tf +++ b/tf-modules/vpc-endpoint/main.tf @@ -1,5 +1,4 @@ # Noop -# Test locals { endpoints = { for k, v in var.endpoints : k => v }