diff --git a/.github/workflows/standalone_publish.yml b/.github/workflows/standalone_publish.yml
index dad8b4c55ba..90b09a98125 100644
--- a/.github/workflows/standalone_publish.yml
+++ b/.github/workflows/standalone_publish.yml
@@ -33,6 +33,7 @@ on:
- patch
- minor
- major
+ - auto
dry-run:
type: boolean
description: 'Skip uploading packages (dry run):'
diff --git a/.github/workflows/standalone_publish_custom.yml b/.github/workflows/standalone_publish_custom.yml
index a939dd8c78f..034301d1304 100644
--- a/.github/workflows/standalone_publish_custom.yml
+++ b/.github/workflows/standalone_publish_custom.yml
@@ -48,6 +48,7 @@ on:
- patch
- minor
- major
+ - auto
only-unpublished:
type: boolean
description: 'Only publish packages that have not yet been published'
diff --git a/lib/node_modules/@stdlib/_tools/changelog/README.md b/lib/node_modules/@stdlib/_tools/changelog/README.md
new file mode 100644
index 00000000000..472511b4896
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/README.md
@@ -0,0 +1,49 @@
+
+
+# Changelog
+
+> Changelog tooling.
+
+
+
+
+
+This directory contains utilities for managing and maintaining changelogs of project packages.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/README.md b/lib/node_modules/@stdlib/_tools/changelog/generate/README.md
new file mode 100644
index 00000000000..3f2791aa8aa
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/README.md
@@ -0,0 +1,125 @@
+
+
+# Generate Changelog
+
+> Generate a changelog for a specified stdlib package.
+
+
+
+## Usage
+
+```javascript
+var generate = require( '@stdlib/_tools/changelog/generate' );
+```
+
+#### generate( pkg\[, releaseType] )
+
+Generates a Markdown formatted changelog for a specified package.
+
+```javascript
+var changelog = generate( '@stdlib/assert/contains' );
+// returns {...}
+```
+
+The function returns an object with the following properties:
+
+- **content**: Markdown formatted changelog.
+- **releaseType**: release type (`null` if changelog is for a non-release).
+
+To generate a changelog for an upcoming release, provide a valid release type as the second argument.
+
+```javascript
+var changelog = generate( '@stdlib/assert/contains', 'patch' );
+// returns {...}
+
+changelog = generate( '@stdlib/assert/contains', 'minor' );
+// returns {...}
+```
+
+The following release types are supported:
+
+- `patch`: a patch release (e.g., bug fixes).
+- `minor`: a minor release (e.g., new features which do not break backward compatibility).
+- `major`: a major release (e.g., breaking changes).
+- `prerelease`: a prerelease (e.g., a release candidate).
+- `prepatch`: a prepatch release.
+- `preminor`: a preminor release.
+- `premajor`: a premajor release.
+- `auto`: automatically determine the release type based on parsed commit messages.
+
+
+
+
+
+
+
+
+
+
+
+## Examples
+
+```javascript
+var generate = require( '@stdlib/_tools/changelog/generate' );
+
+// Generate a changelog for a non-namespace package:
+var changelog = generate( '@stdlib/utils/curry' );
+var content = changelog.content;
+// returns '...'
+
+var releaseType = changelog.releaseType;
+// returns null
+
+// Generate a changelog for a new release:
+changelog = generate( '@stdlib/utils/curry', 'patch' );
+content = changelog.content;
+// returns '...'
+
+releaseType = changelog.releaseType;
+// returns 'patch'
+
+// Generate a changelog for a namespace package:
+changelog = generate( '@stdlib/string/base' );
+content = changelog.content;
+// returns '...'
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js b/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js
new file mode 100644
index 00000000000..43b3f5f8ead
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js
@@ -0,0 +1,47 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+var generate = require( './../lib' );
+
+// Generate a changelog for a non-namespace package:
+var changelog = generate( '@stdlib/utils/curry' );
+var content = changelog.content;
+console.log( content );
+// => '...'
+
+var releaseType = changelog.releaseType;
+console.log( releaseType );
+// => null
+
+// Generate a changelog for a new release:
+changelog = generate( '@stdlib/utils/curry', 'patch' );
+content = changelog.content;
+console.log( content );
+// => '...'
+
+releaseType = changelog.releaseType;
+console.log( releaseType );
+// => 'patch'
+
+// Generate a changelog for a namespace package:
+changelog = generate( '@stdlib/string/base' );
+content = changelog.content;
+console.log( content );
+// => '...'
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js
new file mode 100644
index 00000000000..5c4cad79aee
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js
@@ -0,0 +1,110 @@
+
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MODULES //
+
+var filter = require( '@stdlib/array/base/filter' );
+var trim = require( '@stdlib/string/trim' );
+var map = require( '@stdlib/utils/map' );
+var collectField = require( './collect_field.js' );
+var sectionStart = require( './section_start.js' );
+var sectionEnd = require( './section_end.js' );
+var heading = require( './heading.js' );
+
+
+// VARIABLES //
+
+var STDLIB_GITHUB_URL = 'https://github.com/stdlib-js/stdlib/commit';
+
+
+// FUNCTIONS //
+
+/**
+* Formats a breaking change.
+*
+* @private
+* @param {Object} note - note object
+* @returns {string} changelog entry
+*
+* @example
+* var note = {
+* 'title': 'BREAKING CHANGE',
+* 'text': 'beep',
+* 'hash': 'abcdef1234567890'
+* };
+* var out = formatBreakingChange( note );
+* // returns '- [`abcdef1`](https://github.com/stdlib-js/stdlib/commit/abcdef1234567890): beep'
+*/
+function formatBreakingChange( note ) {
+ var parts = note.text.split( '\n' );
+ var hash = trim( note.hash );
+ var out = '- [`'+hash.substring( 0, 7 )+'`]('+STDLIB_GITHUB_URL+'/'+hash+'): '+parts[ 0 ];
+ if ( parts.length > 1 ) {
+ out +=' \n\n';
+ out += ' - ';
+ out += parts.slice( 1 ).join( '\n ' );
+ out += '\n';
+ }
+ return out;
+}
+
+/**
+* Tests whether a note is a breaking change.
+*
+* @private
+* @param {Object} note - note object
+* @returns {boolean} boolean indicating whether a note is a breaking change
+*/
+function isBreakingChange( note ) {
+ return note.title === 'BREAKING CHANGE';
+}
+
+
+// MAIN //
+
+/**
+* Extracts breaking changes from a list of commits and formats them as a Markdown list.
+*
+* @private
+* @param {ObjectArray} commits - commit objects
+* @returns {string} list of breaking changes
+*/
+function breakingChanges( commits ) {
+ var breakingChanges;
+ var notes;
+ var out;
+
+ notes = collectField( commits, 'notes' );
+ breakingChanges = filter( notes, isBreakingChange );
+ if ( breakingChanges.length === 0 ) {
+ return '';
+ }
+ out = sectionStart( 'breaking-changes' );
+ out += heading( 'BREAKING CHANGES', 3 );
+ out += map( breakingChanges, formatBreakingChange ).join( '\n' );
+ out += sectionEnd( 'breaking-changes' );
+ return out;
+}
+
+
+// EXPORTS //
+
+module.exports = breakingChanges;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/closed_issues.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/closed_issues.js
new file mode 100644
index 00000000000..fb833c9f62b
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/closed_issues.js
@@ -0,0 +1,120 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MODULES //
+
+var filter = require( '@stdlib/array/base/filter' );
+var map = require( '@stdlib/utils/map' );
+var collectField = require( './collect_field.js' );
+var sectionStart = require( './section_start.js' );
+var sectionEnd = require( './section_end.js' );
+var heading = require( './heading.js' );
+
+
+// FUNCTIONS //
+
+/**
+* Tests whether a mention references closing an issue.
+*
+* @private
+* @param {Object} mention - mention object
+* @returns {boolean} boolean indicating whether a mention references closing an issue
+*/
+function isClosingIssue( mention ) {
+ return mention.action === 'Closes' || mention.action === 'Fixes' || mention.action === 'Resolves';
+}
+
+/**
+* Sorts issue mentions by issue number.
+*
+* @private
+* @param {Object} a - first mention
+* @param {Object} b - second mention
+* @returns {number} difference in issue numbers
+*/
+function sortByIssueNumber( a, b ) {
+ return a.ref - b.ref;
+}
+
+/**
+* Formats an issue.
+*
+* @private
+* @param {Object} mention - mention object
+* @returns {string} formatted issue
+*
+* @example
+* var out = formatIssue({
+* 'ref': '123',
+* 'url': 'https://github.com/stdlib-js/stdlib/issues/123'
+* });
+* // returns '[#123](https://github.com/stdlib-js/stdlib/issues/123)'
+*/
+function formatIssue( mention ) {
+ return '[#' + mention.ref + '](' + mention.url + ')';
+}
+
+/**
+* Formats closed issues as a comma-separated list.
+*
+* @private
+* @param {ObjectArray} closedIssues - issue objects
+* @returns {string} formatted issues list
+*/
+function formatClosedIssues( closedIssues ) {
+ var out = sectionStart( 'issues' );
+ out += heading( 'Closed Issues', 3 );
+ if ( closedIssues.length === 1 ) {
+ out += 'This release closes the following issue:\n\n';
+ } else {
+ out += 'A total of ' + closedIssues.length + ' issues were closed in this release:\n\n';
+ }
+ out += map( closedIssues, formatIssue ).join( ', ' );
+ out += sectionEnd( 'issues' );
+ return out;
+}
+
+
+// MAIN //
+
+/**
+* Extracts closed issues from a list of commits and formats them as Markdown.
+*
+* @private
+* @param {ObjectArray} commits - commit objects
+* @returns {string} Markdown list of closed issues
+*/
+function closedIssues( commits ) {
+ var closedIssues;
+ var mentions;
+
+ mentions = collectField( commits, 'mentions' );
+ closedIssues = filter( mentions, isClosingIssue );
+ if ( closedIssues.length === 0 ) {
+ return '';
+ }
+ closedIssues.sort( sortByIssueNumber );
+ return formatClosedIssues( closedIssues );
+}
+
+
+// EXPORTS //
+
+module.exports = closedIssues;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/collect_field.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/collect_field.js
new file mode 100644
index 00000000000..b324e969b70
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/collect_field.js
@@ -0,0 +1,49 @@
+
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MAIN //
+
+/**
+* Collects all field elements from a list of commits.
+*
+* @private
+* @param {ObjectArray} commits - commit objects
+* @param {string} field - field to collect
+* @returns {ObjectArray} field elements
+*/
+function collectField( commits, field ) {
+ var out;
+ var i;
+ var j;
+
+ out = [];
+ for ( i = 0; i < commits.length; i++ ) {
+ for ( j = 0; j < commits[ i ][ field ].length; j++ ) {
+ out.push( commits[ i ][ field ][ j ] );
+ }
+ }
+ return out;
+}
+
+
+// EXPORTS //
+
+module.exports = collectField;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/excluded_contributors.json b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/excluded_contributors.json
new file mode 100644
index 00000000000..293596b822a
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/excluded_contributors.json
@@ -0,0 +1,3 @@
+[
+ "stdlib-bot "
+]
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js
new file mode 100644
index 00000000000..2f5b8544496
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js
@@ -0,0 +1,109 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MODULES //
+
+var replace = require( '@stdlib/string/replace' );
+var trim = require( '@stdlib/string/trim' );
+
+
+// VARIABLES //
+
+var STDLIB_GITHUB_URL = 'https://github.com/stdlib-js/stdlib/commit';
+var RE_CO_AUTHORED_BY = /co-authored-by/i;
+var RE_EMAIL = /\s*<[^>]+>\s*/;
+
+
+// MAIN //
+
+/**
+* Returns a function which formats a commit message for inclusion in a changelog.
+*
+* @private
+* @param {boolean} type - boolean indicating whether to include the commit type
+* @param {boolean} includeAuthors - boolean indicating whether to include the commit author(s)
+* @returns {Function} formatting function
+*
+* @example
+* var f = formatCommitFactory( true, true );
+* // returns
+*/
+function formatCommitFactory( type, includeAuthors ) {
+ return formatCommit;
+
+ /**
+ * Formats a commit message for inclusion in a changelog.
+ *
+ * @private
+ * @param {Object} commit - commit object
+ * @returns {string} changelog entry
+ */
+ function formatCommit( commit ) {
+ var authors = [];
+ var hash = trim( commit.hash );
+ var out = '- [`';
+ out += hash.substring( 0, 7 );
+ out += '`](';
+ out += STDLIB_GITHUB_URL;
+ out += '/';
+ out += hash;
+ out += ') - ';
+ if ( commit.author ) {
+ authors.push( replace( commit.author, RE_EMAIL, '' ) );
+ }
+ if ( type ) {
+ out += '**';
+ out += commit.type;
+ if ( commit.scope ) {
+ out += '(' + commit.scope + ')';
+ }
+ out += ':** ';
+ } else if ( commit.scope ) {
+ out += '**' + commit.scope + ':** ';
+ }
+ out += commit.subject;
+ if ( commit.mentions && commit.mentions.length > 0 ) {
+ commit.mentions.forEach( onMention );
+ }
+ if ( includeAuthors ) {
+ out += ' _(by ' + authors.join( ', ' ) + ')_';
+ }
+ return out;
+
+ /**
+ * Callback invoked upon encountering a mention.
+ *
+ * @private
+ * @param {Object} mention - mention object
+ */
+ function onMention( mention ) {
+ if ( mention.action === 'PR-URL' ) {
+ out += ' [(#' + trim( mention.ref ) + ')](' + mention.url + ')';
+ } else if ( RE_CO_AUTHORED_BY.test( mention.action ) ) {
+ authors.push( replace( mention.ref, RE_EMAIL, '' ) );
+ }
+ }
+ }
+}
+
+
+// EXPORTS //
+
+module.exports = formatCommitFactory;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commits.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commits.js
new file mode 100644
index 00000000000..93c8ab899cd
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commits.js
@@ -0,0 +1,52 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MODULES //
+
+var map = require( '@stdlib/utils/map' );
+var formatCommitFactory = require( './format_commit_factory.js' );
+var sectionStart = require( './section_start.js' );
+var sectionEnd = require( './section_end.js' );
+var heading = require( './heading.js' );
+
+
+// MAIN //
+
+/**
+* Formats a list of commits as a Markdown string.
+*
+* @private
+* @param {ObjectArray} commits - commits
+* @returns {string} Markdown string
+*/
+function formatCommits( commits ) {
+ var out = sectionStart( 'commits' );
+ out += heading( 'Commits', 3 );
+ out += '\n\n';
+ out += map( commits, formatCommitFactory( true, true ) ).join( '\n' );
+ out += '\n\n ';
+ out += sectionEnd( 'commits' );
+ return out;
+}
+
+
+// EXPORTS //
+
+module.exports = formatCommits;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js
new file mode 100644
index 00000000000..f69a318ab56
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js
@@ -0,0 +1,118 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MODULES //
+
+var contains = require( '@stdlib/assert/contains' );
+var replace = require( '@stdlib/string/replace' );
+var map = require( '@stdlib/utils/map' );
+var sectionStart = require( './section_start.js' );
+var sectionEnd = require( './section_end.js' );
+var heading = require( './heading.js' );
+var EXCLUDED_CONTRIBUTORS = require( './excluded_contributors.json' );
+
+
+// VARIABLES //
+
+var RE_CO_AUTHORED_BY = /co-authored-by/i;
+
+
+// FUNCTIONS //
+
+/**
+* Extracts a list of contributors from a list of commits.
+*
+* @private
+* @param {ObjectArray} commits - commit objects
+* @returns {StringArray} sorted list of contributors
+*/
+function extractContributors( commits ) {
+ var mentions;
+ var mention;
+ var out;
+ var i;
+ var j;
+
+ out = [];
+ for ( i = 0; i < commits.length; i++ ) {
+ if (
+ !contains( out, commits[ i ].author ) &&
+ !contains( EXCLUDED_CONTRIBUTORS, commits[ i ].author )
+ ) {
+ out.push( commits[ i ].author );
+ }
+ mentions = commits[ i ].mentions || [];
+ for ( j = 0; j < mentions.length; j++ ) {
+ mention = mentions[ j ];
+ if (
+ RE_CO_AUTHORED_BY.test( mention.action ) &&
+ !contains( out, mention.ref ) &&
+ !contains( EXCLUDED_CONTRIBUTORS, mention.ref )
+ ) {
+ out.push( mention.ref );
+ }
+ }
+ }
+ return out.sort();
+}
+
+/**
+* Formats a contributor.
+*
+* @private
+* @param {string} contributor - contributor
+* @returns {string} formatted contributor
+*
+* @example
+* var out = formatContributor( 'Jane Doe ' );
+* // returns '- Jane Doe'
+*/
+function formatContributor( contributor ) {
+ return '- ' + replace( contributor, /\s*<[^>]+>\s*/, '' );
+}
+
+
+// MAIN //
+
+/**
+* Formats a list of contributors.
+*
+* @private
+* @param {ObjectArray} commits - commit objects
+* @returns {string} formatted contributors list
+*/
+function formatContributors( commits ) {
+ var contributors = extractContributors( commits );
+ var out = sectionStart( 'contributors' );
+ out += heading( 'Contributors', 3 );
+ if ( contributors.length === 1 ) {
+ out += 'A total of 1 person contributed to this release. Thank you to this contributor:\n\n';
+ } else {
+ out += 'A total of ' + contributors.length + ' people contributed to this release. Thank you to the following contributors:\n\n';
+ }
+ out += map( contributors, formatContributor ).join( '\n' );
+ out += sectionEnd( 'contributors' );
+ return out;
+}
+
+
+// EXPORTS //
+
+module.exports = formatContributors;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/heading.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/heading.js
new file mode 100644
index 00000000000..04394a1ebdb
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/heading.js
@@ -0,0 +1,54 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MODULES //
+
+var repeat = require( '@stdlib/string/repeat' );
+
+
+// MAIN //
+
+/**
+* Returns a Markdown heading.
+*
+* @private
+* @param {string} text - heading
+* @param {number} level - heading level
+* @returns {string} Markdown heading
+*
+* @example
+* var out = heading( 'beep', 1 );
+* // returns '\n\n# beep\n\n'
+*
+* @example
+* var out = heading( 'boop', 3 );
+* // returns '\n\n### boop\n\n'
+*/
+function heading( text, level ) {
+ var str = '\n\n';
+ str += repeat( '#', level ) + ' ' + text;
+ str += '\n\n';
+ return str;
+}
+
+
+// EXPORTS //
+
+module.exports = heading;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/index.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/index.js
new file mode 100644
index 00000000000..0d55e217101
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/index.js
@@ -0,0 +1,45 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+/**
+* Generate a changelog for a specified stdlib package.
+*
+* @module @stdlib/_tools/changelog/generate
+*
+* @example
+* var generateChangelog = require( '@stdlib/_tools/changelog/generate' );
+*
+* // Standard packages:
+* var changelog = generateChangelog( '@stdlib/utils/curry' );
+* // returns {...}
+*
+* // Namespace packages:
+* changelog = generateChangelog( '@stdlib/stats/base/dists' );
+* // returns {...}
+*/
+
+// MODULES //
+
+var main = require( './main.js' );
+
+
+// EXPORTS //
+
+module.exports = main;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js
new file mode 100644
index 00000000000..92040e0509f
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js
@@ -0,0 +1,398 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MODULES //
+
+var dirname = require( 'path' ).dirname;
+var join = require( 'path' ).join;
+var semver = require( 'semver' );
+var recommendVersionBump = require( '@stdlib/_tools/changelog/recommend-version-bump' );
+var name2standalone = require( '@stdlib/_tools/pkgs/name2standalone' );
+var substringAfter = require( '@stdlib/string/substring-after' );
+var objectEntries = require( '@stdlib/utils/entries' );
+var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' );
+var objectKeys = require( '@stdlib/utils/keys' );
+var namespaces = require( '@stdlib/_tools/pkgs/namespaces' ).sync;
+var contains = require( '@stdlib/assert/contains' );
+var isString = require( '@stdlib/assert/is-string' ).isPrimitive;
+var rootDir = require( '@stdlib/_tools/utils/root-dir' );
+var replace = require( '@stdlib/string/replace' );
+var groupBy = require( '@stdlib/utils/group-by' );
+var filter = require( '@stdlib/array/base/filter' );
+var format = require( '@stdlib/string/format' );
+var repeat = require( '@stdlib/string/repeat' );
+var isNull = require( '@stdlib/assert/is-null' );
+var formatContributors = require( './format_contributors.js' );
+var breakingChanges = require( './breaking_changes.js' );
+var closedIssues = require( './closed_issues.js' );
+var releaseSummary = require( './release_summary.js' );
+var formatCommits = require( './format_commits.js' );
+var npmReleases = require( './npm_releases.js' );
+var sectionStart = require( './section_start.js' );
+var sectionEnd = require( './section_end.js' );
+var heading = require( './heading.js' );
+
+
+// VARIABLES //
+
+var STDLIB_LIB_DIR = join( rootDir(), 'lib', 'node_modules', '@stdlib' );
+var STDLIB_NAMESPACE_PKGS = namespaces();
+var STDLIB_REPO_NODE_PATH = 'https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules';
+var RE_PACKAGE_SUBDIRS = /\/?(benchmark|bin|data|docs|etc|examples|include|lib|scripts|src|test)\/?[\s\S]*$/;
+var RE_MARKDOWN_HEADER = /(^#+)/gm;
+var RE_EXTRANEOUS_NEWLINES = /\n{3,}/g;
+
+
+// FUNCTIONS //
+
+/**
+* Decrements all Markdown headers by a specified number of levels.
+*
+* @private
+* @param {string} str - string to modify
+* @param {number} n - number of levels to decrement
+* @returns {string} modified string
+*
+* @example
+* var str = '## Foo\n\n### Bar\n';
+* var out = decrementHeaders( str, 1 );
+* // returns '### Foo\n\n#### Bar\n'
+*
+* @example
+* var str = '# Beep Boop\n\n## Baz Bar';
+* var out = decrementHeaders( str, 2 );
+* // returns '### Beep Boop\n\n#### Baz Bar'
+*/
+function decrementHeaders( str, n ) {
+ return replace( str, RE_MARKDOWN_HEADER, '$1' + repeat( '#', n ) );
+}
+
+/**
+* Formats a given date as a string in the format YYYY-MM-DD.
+*
+* @private
+* @param {(string|integer)} [date] - date to format (default: current date)
+* @returns {string} formatted date string in YYYY-MM-DD format
+*/
+function formatDate( date ) {
+ return new Date( date || Date.now() ).toISOString().slice( 0, 10 );
+}
+
+/**
+* Generates an opening release section tag.
+*
+* @private
+* @param {string} [version] - release version
+* @returns {string} release section tag
+*/
+function releaseSectionStart( version ) {
+ if ( version ) {
+ return '\n\n';
+ }
+ return '\n\n';
+}
+
+/**
+* Groups commits by subpackages.
+*
+* @private
+* @param {ObjectArray} commits - commits
+* @param {string} pkg - package name
+* @returns {Object} grouped commits
+*/
+function groupBySubPackage( commits, pkg ) {
+ var commit;
+ var group;
+ var out;
+ var i;
+ var j;
+
+ out = {};
+ for ( i = 0; i < commits.length; i++ ) {
+ commit = commits[ i ];
+ for ( j = 0; j < commit.files.length; j++ ) {
+ if ( commit.files[ j ].indexOf( 'lib/node_modules/@stdlib/' ) !== -1 ) {
+ group = dirname( commit.files[ j ] );
+ group = substringAfter( group, ( pkg ) ? pkg + '/' : '@stdlib/' );
+ group = replace( group, RE_PACKAGE_SUBDIRS, '' );
+ if ( contains( group, '/_tools/' ) ) {
+ continue;
+ }
+ if ( !out[ group ] ) {
+ out[ group ] = [];
+ }
+ if ( !contains( out[ group ], commit ) ) {
+ out[ group ].push( commit );
+ }
+ }
+ }
+ }
+ return out;
+}
+
+/**
+* Tests whether a commit only affects internal tooling, i.e., files located in the `lib/node_modules/@stdlib/_tools/` directory.
+*
+* @private
+* @param {Object} commit - commit object
+* @returns {boolean} boolean indicating whether a commit is not only touching internal tooling
+*/
+function isNotInternalTooling( commit ) {
+ var files;
+ var i;
+
+ files = commit.files;
+ for ( i = 0; i < files.length; i++ ) {
+ if ( files[ i ].indexOf( 'lib/node_modules/@stdlib/_tools/' ) === -1 ) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+* Wraps a package summary for inclusion in a namespace changelog.
+*
+* @private
+* @param {string} pkg - namespace package
+* @param {string} version - namespace version
+* @param {string} name - subpackage name
+* @param {string} summary - package summary
+* @returns {string} wrapped package summary
+*/
+function packageSummaryWrapper( pkg, version, name, summary ) {
+ var href;
+ var out;
+ var id;
+
+ name = pkg + ( ( name ) ? '/' + name : '' );
+ href = STDLIB_REPO_NODE_PATH + '/%40stdlib/' + name;
+ id = replace( name, '/', '-' );
+ if ( version ) {
+ id += '-v' + version;
+ } else {
+ id += '-unreleased';
+ }
+ out = '\n\n';
+ out += heading( '[@stdlib/' + name + '](' + href + ')', 4 );
+ out += '\n\n';
+ out += decrementHeaders( summary, 2 );
+ out += ' ';
+ out += sectionEnd( 'package' );
+ return out;
+}
+
+
+// MAIN //
+
+/**
+* Generates a Markdown formatted changelog for a specified stdlib package.
+*
+* @param {string} pkg - package name
+* @param {string} [releaseType] - release type (`patch`, `minor`, `major`, `prerelease`, `prepatch`, `preminor`, `premajor`, or `auto`)
+* @throws {TypeError} must provide a string
+* @throws {Error} must provide a valid package name
+* @throws {TypeError} must provide a recognized release type
+* @returns {Object} changelog `content` and `releaseType` (`null` if no release)
+*
+* @example
+* var changelog = generate( '@stdlib/utils/curry' );
+* // returns {...}
+*
+* @example
+* var changelog = generate( '@stdlib/stats/base/dists' );
+* // returns {...}
+*
+* @example
+* var changelog = generate( '@stdlib/proxy' );
+* // returns {...}
+*
+* @example
+* var changelog = generate( '@stdlib/utils/curry', 'patch' );
+* // returns {...}
+*
+* @example
+* var changelog = generate( '@stdlib/utils/curry', 'major' );
+* // returns {...}
+*/
+function generate( pkg, releaseType ) {
+ var isNamespacePkg;
+ var releaseCommits;
+ var newestRelease;
+ var bySubpackage;
+ var nextVersion;
+ var standalone;
+ var unreleased;
+ var releases;
+ var pkgNames;
+ var commits;
+ var version;
+ var summary;
+ var name;
+ var str;
+ var i;
+ var j;
+
+ if ( !isString( pkg ) ) {
+ throw new TypeError( format( 'invalid argument. Must provide a string. Value: `%s`.', pkg ) );
+ }
+ if ( pkg === '@stdlib' || pkg === '@stdlib/stdlib' ) {
+ // Case: root package
+ isNamespacePkg = true;
+ standalone = '@stdlib/stdlib';
+ pkg = '';
+ releases = objectEntries( npmReleases( standalone ) );
+ } else {
+ // Case: all other packages
+ isNamespacePkg = contains( STDLIB_NAMESPACE_PKGS, pkg );
+ standalone = name2standalone( pkg );
+ pkg = replace( pkg, '@stdlib/', '' );
+ releases = objectEntries( npmReleases( standalone ) );
+ }
+
+ str = '# CHANGELOG\n\n';
+ str += '> Package changelog.\n\n';
+
+ commits = parseCommits({
+ 'dir': join( STDLIB_LIB_DIR, pkg )
+ });
+ if ( commits.length === 0 ) {
+ throw new Error( format( 'invalid argument. Unable to parse commits for package: `%s`.', pkg ) );
+ }
+
+ // Filter out all commits which only affect internal tooling:
+ commits = filter( commits, isNotInternalTooling );
+
+ // Group commits by release:
+ commits = groupBy( commits, indicator );
+ if ( !commits.unreleased ) {
+ commits.unreleased = [];
+ }
+
+ if ( releaseType === 'auto' ) {
+ releaseType = recommendVersionBump( commits.unreleased );
+ }
+ if ( releaseType ) {
+ newestRelease = releases[ releases.length-1 ][ 0 ];
+ nextVersion = semver.inc( newestRelease, releaseType );
+ if ( isNull( nextVersion ) ) {
+ throw new TypeError( format( 'invalid argument. Unrecognized release type: `%s`.', releaseType ) );
+ }
+ }
+
+ if ( isNamespacePkg ) {
+ str += releaseSectionStart( nextVersion );
+ str += '## ' + ( nextVersion || 'Unreleased' ) + ' (' + formatDate() + ')\n\n';
+ bySubpackage = groupBySubPackage( commits.unreleased, pkg );
+ pkgNames = objectKeys( bySubpackage ).sort();
+ str += sectionStart( 'packages' );
+ str += heading( 'Packages', 3 );
+ for ( i = 0; i < pkgNames.length; i++ ) {
+ name = pkgNames[ i ];
+ unreleased = releaseSummary( bySubpackage[ name ], true, true );
+ if ( unreleased ) {
+ str += packageSummaryWrapper( pkg, '', name, unreleased );
+ }
+ }
+ str += sectionEnd( 'packages' );
+ str += breakingChanges( commits.unreleased );
+ str += closedIssues( commits.unreleased );
+ str += formatContributors( commits.unreleased );
+ str += formatCommits( commits.unreleased );
+ str += sectionEnd( 'release' );
+ } else {
+ unreleased = releaseSummary( commits.unreleased );
+ if ( unreleased ) {
+ str += releaseSectionStart( nextVersion );
+ str += '## ' + ( nextVersion || 'Unreleased' ) + ' (' + formatDate() + ')\n\n';
+ str += unreleased;
+ str += sectionEnd( 'release' );
+ }
+ }
+ if ( isNamespacePkg ) {
+ for ( i = releases.length-1; i >= 0; i-- ) {
+ version = releases[ i ][ 0 ];
+ releaseCommits = commits[ version ];
+ if ( !releaseCommits ) {
+ continue;
+ }
+ str += '## ' + version + ' (' + formatDate( releases[ i ][ 1 ] ) + ')\n\n';
+ bySubpackage = groupBySubPackage( releaseCommits, pkg );
+ pkgNames = objectKeys( bySubpackage ).sort();
+ str += sectionStart( 'packages' );
+ str += heading( 'Packages', 3 );
+ for ( j = 0; j < pkgNames.length; j++ ) {
+ name = pkgNames[ j ];
+ summary = releaseSummary( bySubpackage[ name ], true, true );
+ if ( !summary ) {
+ continue;
+ }
+ str += packageSummaryWrapper( pkg, version, name, summary );
+ }
+ str += sectionEnd( 'packages' );
+ str += breakingChanges( releaseCommits );
+ str += closedIssues( releaseCommits );
+ str += formatContributors( releaseCommits );
+ str += formatCommits( releaseCommits );
+ }
+ } else {
+ for ( i = releases.length-1; i >= 0; i-- ) {
+ version = releases[ i ][ 0 ];
+ summary = releaseSummary( commits[ version ] );
+ if ( !summary ) {
+ continue;
+ }
+ str += releaseSectionStart( version );
+ str += '## ' + version + ' (' + formatDate( releases[ i ][ 1 ] ) + ')\n\n';
+ str += summary;
+ str += sectionEnd( 'release' );
+ }
+ }
+ return {
+ 'content': replace( str, RE_EXTRANEOUS_NEWLINES, '\n\n' ),
+ 'releaseType': releaseType || null
+ };
+
+ /**
+ * Returns an indicator for a commit based on its release status.
+ *
+ * @private
+ * @param {Object} commit - commit object
+ * @returns {string} release indicator
+ */
+ function indicator( commit ) {
+ var date;
+ var i;
+ date = new Date( commit.date );
+
+ // Walk the releases in reverse chronological order:
+ for ( i = releases.length-1; i >= 0; i-- ) {
+ if ( date <= new Date( releases[ i ][ 1 ] ) ) {
+ return releases[ i ][ 0 ]; // version
+ }
+ }
+ return 'unreleased';
+ }
+}
+
+
+// EXPORTS //
+
+module.exports = generate;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js
new file mode 100644
index 00000000000..6e455b48e8d
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js
@@ -0,0 +1,72 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MODULES //
+
+var shell = require( 'child_process' ).execSync; // eslint-disable-line node/no-sync
+var logger = require( 'debug' );
+var omit = require( '@stdlib/utils/omit' );
+var rootDir = require( '@stdlib/_tools/utils/root-dir' );
+
+
+// VARIABLES //
+
+var debug = logger( 'changelog:generate' );
+var OMITTED_KEYS = [ 'created', 'modified', 'unpublished' ];
+
+
+// MAIN //
+
+/**
+* Returns a list of published package versions and their release dates.
+*
+* @private
+* @param {string} pkg - package name
+* @returns {Object} object mapping versions to release dates
+*
+* @example
+* var releases = npmReleases( '@stdlib/utils-omit' );
+* // returns {...}
+*/
+function npmReleases( pkg ) {
+ var command;
+ var opts;
+ var out;
+
+ command = 'npm view ' + pkg + ' time --json';
+ opts = {
+ 'cwd': rootDir(),
+ 'stdio': [ 'pipe', 'pipe', 'ignore' ] // stdin, stdout, stderr
+ };
+ try {
+ out = shell( command, opts ).toString();
+ out = JSON.parse( out );
+ } catch ( err ) {
+ debug( 'Encountered an error when attempting to retrieve package release dates: %s', err.message );
+ return {};
+ }
+ out = omit( out, OMITTED_KEYS );
+ return out;
+}
+
+
+// EXPORTS //
+
+module.exports = npmReleases;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js
new file mode 100644
index 00000000000..e96989e9d69
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js
@@ -0,0 +1,116 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MODULES //
+
+var groupBy = require( '@stdlib/utils/group-by' );
+var map = require( '@stdlib/utils/map' );
+var formatCommitFactory = require( './format_commit_factory.js' );
+var formatContributors = require( './format_contributors.js' );
+var formatCommits = require( './format_commits.js' );
+var breakingChanges = require( './breaking_changes.js' );
+var closedIssues = require( './closed_issues.js' );
+var sectionStart = require( './section_start.js' );
+var sectionEnd = require( './section_end.js' );
+var heading = require( './heading.js' );
+
+
+// FUNCTIONS //
+
+/**
+* Groups commits by type.
+*
+* @private
+* @param {Object} commit - commit object
+* @returns {(string|null)} group key
+*/
+function groupByType( commit ) {
+ return commit.type;
+}
+
+
+// MAIN //
+
+/**
+* Generates a release summary.
+*
+* @private
+* @param {ObjectArray} commits - commit objects
+* @param {boolean} excludeCommits - boolean indicating whether to exclude commit details
+* @param {boolean} excludeContributors - boolean indicating whether to exclude a list of contributors
+* @returns {string} summary
+*/
+function releaseSummary( commits, excludeCommits, excludeContributors ) {
+ var groupedFormat;
+ var grouped;
+ var out;
+
+ if ( !commits || !commits.length ) {
+ return '';
+ }
+ grouped = groupBy( commits, groupByType );
+ groupedFormat = formatCommitFactory( false, false );
+
+ out = '';
+ if ( grouped.feat ) {
+ out += sectionStart( 'features' );
+ out += heading( 'Features', 3 );
+ out += map( grouped.feat, groupedFormat ).join( '\n' );
+ out += sectionEnd( 'features' );
+ }
+ if ( grouped.fix ) {
+ out += sectionStart( 'bug-fixes' );
+ out += heading( 'Bug Fixes', 3 );
+ out += map( grouped.fix, groupedFormat ).join( '\n' );
+ out += sectionEnd( 'bug-fixes' );
+ }
+ if ( grouped.perf ) {
+ out += sectionStart( 'performance' );
+ out += heading( 'Performance', 3 );
+ out += map( grouped.perf, groupedFormat ).join( '\n' );
+ out += sectionEnd( 'performance' );
+ }
+ if ( grouped.revert ) {
+ out += sectionStart( 'reverts' );
+ out += heading( 'Reverts', 3 );
+ out += map( grouped.revert, groupedFormat ).join( '\n' );
+ out += sectionEnd( 'reverts' );
+ }
+ if ( grouped.deprecate ) {
+ out += sectionStart( 'deprecations' );
+ out += heading( 'Deprecations', 3 );
+ out += map( grouped.deprecate, groupedFormat ).join( '\n' );
+ out += sectionEnd( 'deprecations' );
+ }
+ out += breakingChanges( commits );
+ out += closedIssues( commits );
+ if ( !excludeCommits ) {
+ out += formatCommits( commits );
+ }
+ if ( !excludeContributors ) {
+ out += formatContributors( commits );
+ }
+ return out;
+}
+
+
+// EXPORTS //
+
+module.exports = releaseSummary;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/section_end.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/section_end.js
new file mode 100644
index 00000000000..e8252c6fafc
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/section_end.js
@@ -0,0 +1,37 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MAIN //
+
+/**
+* Generates a closing section tag.
+*
+* @private
+* @param {string} name - section name
+* @returns {string} closing section tag
+*/
+function sectionEnd( name ) {
+ return '\n\n\n\n\n\n';
+}
+
+
+// EXPORTS //
+
+module.exports = sectionEnd;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/section_start.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/section_start.js
new file mode 100644
index 00000000000..bd960e8e188
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/section_start.js
@@ -0,0 +1,37 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MAIN //
+
+/**
+* Generates a starting section tag.
+*
+* @private
+* @param {string} name - section name
+* @returns {string} starting section tag
+*/
+function sectionStart( name ) {
+ return '\n\n\n\n';
+}
+
+
+// EXPORTS //
+
+module.exports = sectionStart;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/package.json b/lib/node_modules/@stdlib/_tools/changelog/generate/package.json
new file mode 100644
index 00000000000..313dfeb9b77
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/package.json
@@ -0,0 +1,62 @@
+{
+ "name": "@stdlib/_tools/changelog/generate",
+ "version": "0.0.0",
+ "description": "Generate a changelog for a specified stdlib package.",
+ "license": "Apache-2.0",
+ "author": {
+ "name": "The Stdlib Authors",
+ "url": "https://github.com/stdlib-js/stdlib/graphs/contributors"
+ },
+ "contributors": [
+ {
+ "name": "The Stdlib Authors",
+ "url": "https://github.com/stdlib-js/stdlib/graphs/contributors"
+ }
+ ],
+ "main": "./lib",
+ "directories": {
+ "example": "./examples",
+ "lib": "./lib",
+ "test": "./test"
+ },
+ "scripts": {},
+ "homepage": "https://github.com/stdlib-js/stdlib",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/stdlib-js/stdlib.git"
+ },
+ "bugs": {
+ "url": "https://github.com/stdlib-js/stdlib/issues"
+ },
+ "dependencies": {},
+ "devDependencies": {},
+ "engines": {
+ "node": ">=0.10.0",
+ "npm": ">2.7.0"
+ },
+ "os": [
+ "aix",
+ "darwin",
+ "freebsd",
+ "linux",
+ "macos",
+ "openbsd",
+ "sunos",
+ "win32",
+ "windows"
+ ],
+ "keywords": [
+ "stdlib",
+ "tools",
+ "tool",
+ "changelog",
+ "generate",
+ "generator",
+ "commit",
+ "commits",
+ "conventional",
+ "markdown",
+ "md"
+ ],
+ "__stdlib__": {}
+}
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js b/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js
new file mode 100644
index 00000000000..9e40c88bea1
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js
@@ -0,0 +1,157 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MODULES //
+
+var tape = require( 'tape' );
+var isObject = require( '@stdlib/assert/is-plain-object' );
+var generate = require( './../lib' );
+
+
+// TESTS //
+
+tape( 'main export is a function', function test( t ) {
+ t.ok( true, __filename );
+ t.equal( typeof generate, 'function', 'main export is a function' );
+ t.end();
+});
+
+tape( 'the function throws an error if not provided a string', function test( t ) {
+ var values;
+ var i;
+
+ values = [
+ 5,
+ null,
+ true,
+ void 0,
+ NaN,
+ [],
+ {},
+ function noop() {}
+ ];
+
+ for ( i = 0; i < values.length; i++ ) {
+ t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
+ }
+ t.end();
+
+ function badValue( value ) {
+ return function badValue() {
+ generate( value );
+ };
+ }
+});
+
+tape( 'the function throws an error if provided an unrecognized package name', function test( t ) {
+ var values;
+ var i;
+
+ values = [
+ 'beep',
+ 'beep/boop',
+ 'foo/bar/baz'
+ ];
+
+ for ( i = 0; i < values.length; i++ ) {
+ t.throws( badValue( values[i] ), Error, 'throws an error when provided '+values[i] );
+ }
+ t.end();
+
+ function badValue( value ) {
+ return function badValue() {
+ generate( value );
+ };
+ }
+});
+
+tape( 'the function throws an error if provided an invalid release type', function test( t ) {
+ var values;
+ var i;
+
+ values = [
+ 'massive',
+ 'big',
+ 'beep',
+ 'boop',
+ 'patching'
+ ];
+
+ for ( i = 0; i < values.length; i++ ) {
+ t.throws( badValue( values[i] ), Error, 'throws an error when provided '+values[i] );
+ }
+ t.end();
+
+ function badValue( value ) {
+ return function badValue() {
+ generate( '@stdlib/utils/curry', value );
+ };
+ }
+});
+
+tape( 'the function generates a changelog', function test( t ) {
+ var out = generate( '@stdlib/utils/curry' );
+ t.strictEqual( isObject( out ), true, 'returns expected value' );
+ t.strictEqual( typeof out.content, 'string', 'returns expected value' );
+ t.strictEqual( out.releaseType, null, 'returns expected value' );
+
+ out = generate( '@stdlib/stats/base/dists' );
+ t.strictEqual( isObject( out ), true, 'returns expected value' );
+ t.strictEqual( typeof out.content, 'string', 'returns expected value' );
+ t.strictEqual( out.releaseType, null, 'returns expected value' );
+
+ out = generate( '@stdlib/proxy' );
+ t.strictEqual( isObject( out ), true, 'returns expected value' );
+ t.strictEqual( typeof out.content, 'string', 'returns expected value' );
+ t.strictEqual( out.releaseType, null, 'returns expected value' );
+
+ t.end();
+});
+
+tape( 'the function generates a changelog for a new release', function test( t ) {
+ var out = generate( '@stdlib/utils/noop', 'patch' );
+ t.strictEqual( isObject( out ), true, 'returns expected value' );
+ t.strictEqual( typeof out.content, 'string', 'returns expected value' );
+ t.strictEqual( out.releaseType, 'patch', 'returns expected value' );
+
+ out = generate( '@stdlib/utils/noop', 'minor' );
+ t.strictEqual( isObject( out ), true, 'returns expected value' );
+ t.strictEqual( typeof out.content, 'string', 'returns expected value' );
+ t.strictEqual( out.releaseType, 'minor', 'returns expected value' );
+
+ out = generate( '@stdlib/utils/noop', 'major' );
+ t.strictEqual( isObject( out ), true, 'returns expected value' );
+ t.strictEqual( typeof out.content, 'string', 'returns expected value' );
+ t.strictEqual( out.releaseType, 'major', 'returns expected value' );
+
+ t.end();
+});
+
+tape( 'the function generates a changelog for a new release (auto)', function test( t ) {
+ var out = generate( '@stdlib/utils/noop', 'auto' );
+ t.strictEqual( isObject( out ), true, 'returns expected value' );
+ t.strictEqual( typeof out.content, 'string', 'returns expected value' );
+
+ out = generate( '@stdlib/array/base/slice', 'auto' );
+ t.strictEqual( isObject( out ), true, 'returns expected value' );
+ t.strictEqual( typeof out.content, 'string', 'returns expected value' );
+
+ t.end();
+});
diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/README.md b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/README.md
new file mode 100644
index 00000000000..9a6ea4f2dd0
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/README.md
@@ -0,0 +1,92 @@
+
+
+# Parse Commits
+
+> Parse commit messages into conventional changelog objects.
+
+
+
+## Usage
+
+```javascript
+var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' );
+```
+
+#### parseCommits( \[options] )
+
+Parses commit messages into conventional changelog objects.
+
+```javascript
+var commits = parseCommits();
+// returns [...]
+```
+
+The function accepts the following `options`:
+
+- **dir**: directory for which to parse commits. May be either an absolute path or a path relative to the current working directory. Default: current working directory.
+- **issueURL**: issue URL. Default: `https://github.com/stdlib-js/stdlib/issues/`.
+- **prURL**: pull request URL. Default: `https://github.com/stdlib-js/stdlib/pull/`.
+
+
+
+
+
+
+
+
+
+
+
+## Examples
+
+```javascript
+var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' );
+
+var commits = parseCommits();
+// returns [...]
+
+commits = parseCommits({
+ 'dir': '/path/to/non-existent/directory'
+});
+// returns []
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/examples/index.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/examples/index.js
new file mode 100644
index 00000000000..838b1b83588
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/examples/index.js
@@ -0,0 +1,31 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+var parseCommits = require( './../lib' );
+
+var commits = parseCommits();
+console.log( commits );
+// => [...]
+
+commits = parseCommits({
+ 'dir': '/path/to/non-existent/directory'
+});
+console.log( commits );
+// => []
diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js
new file mode 100644
index 00000000000..2cc66c27bc9
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js
@@ -0,0 +1,115 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MODULES //
+
+var resolve = require( 'path' ).resolve;
+var join = require( 'path' ).join;
+var spawn = require( 'child_process' ).spawnSync; // eslint-disable-line node/no-sync
+var logger = require( 'debug' );
+var replace = require( '@stdlib/string/replace' );
+var trim = require( '@stdlib/string/trim' );
+
+
+// VARIABLES //
+
+var debug = logger( 'changelog:parse-commits' );
+var GIT_COMMIT_SEP = '^---^';
+
+
+// FUNCTIONS //
+
+/**
+* Escapes double quotes in a string.
+*
+* @private
+* @param {string} str - input string
+* @returns {string} escaped string
+*
+* @example
+* var str = escapeDoubleQuotes( 'beep "boop"' );
+* // returns 'beep \"boop\"'
+*/
+function escapeDoubleQuotes( str ) {
+ return replace( str, '"', '\\"' );
+}
+
+
+// MAIN //
+
+/**
+* Extracts commit details for a specified directory.
+*
+* @private
+* @param {string} dir - directory for which to extract commit details
+* @returns {Array} array of commit details
+*
+* @example
+* var commits = extractCommits( '.' );
+* // returns [...]
+*/
+function extractCommits( dir ) {
+ var commits;
+ var lines;
+ var parts;
+ var args;
+ var opts;
+ var cmd;
+ var out;
+ var i;
+
+ opts = {
+ 'maxBuffer': 1024*1024*60, // 60 MB
+ 'stdio': [ 'pipe', 'pipe', 'ignore' ], // stdin, stdout, stderr
+ 'env': {
+ 'GIT_COMMIT_SEP': GIT_COMMIT_SEP
+ }
+ };
+ cmd = join( __dirname, '..', 'scripts', 'commits.sh' );
+ commits = [];
+ args = [ resolve( dir ) ];
+ try {
+ out = spawn( cmd, args, opts );
+ } catch ( err ) {
+ debug( 'Encountered an error when attempting to extract commits: %s', err.message );
+ return commits;
+ }
+ out = trim( out.stdout.toString() );
+ lines = out.split( GIT_COMMIT_SEP );
+ for ( i = 0; i < lines.length; i++ ) {
+ if ( !lines[ i ] ) {
+ continue;
+ }
+ parts = lines[ i ].split( '|' );
+ commits.push({
+ 'hash': trim( parts[ 0 ] ),
+ 'date': parts[ 1 ],
+ 'author': parts[ 2 ],
+ 'message': trim( escapeDoubleQuotes( parts[ 3 ] ) ),
+ 'files': trim( parts[ 4 ] ).split( '\n' )
+ });
+ }
+ return commits;
+}
+
+
+// EXPORTS //
+
+module.exports = extractCommits;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js
new file mode 100644
index 00000000000..b9089e4cdfb
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js
@@ -0,0 +1,274 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MODULES //
+
+var visitWithAncestors = require( 'unist-util-visit-parents' );
+var visit = require( 'unist-util-visit' );
+
+
+// VARIABLES //
+
+var RE_DIGITS = /^[0-9]+$/;
+var RE_CLOSES = /(?:closes|fixes|resolves)/i;
+var RE_CO_AUTHORED_BY = /co-authored-by/i;
+
+
+// MAIN //
+
+/**
+* Converts a commit message AST to the conventional changelog format.
+*
+* @private
+* @param {Object} ast - unist commit message AST
+* @param {Options} options - function options
+* @param {string} options.issueURL - issue URL
+* @param {string} options.prURL - PR URL
+* @returns {Object} conventional changelog format
+*/
+function toConventionalChangelog( ast, options ) {
+ var out = {
+ 'type': '',
+ 'scope': null,
+ 'header': '',
+ 'subject': '',
+ 'body': '',
+ 'notes': [],
+ 'references': [],
+ 'mentions': [],
+ 'footer': null
+ };
+
+ visit( ast, 'body', onBody );
+ visit( ast, 'summary', onSummary );
+ visitWithAncestors( ast, [ 'breaking-change' ], processBreakingChanges );
+ visit( ast, 'footer', processFooter );
+
+ return out;
+
+ /**
+ * Callback invoked upon visiting a breaking change node.
+ *
+ * @private
+ * @param {Object} _ - AST node
+ * @param {ObjectArray} ancestors - node ancestors
+ */
+ function processBreakingChanges( _, ancestors ) {
+ var startCollecting = false;
+ var breaking = {
+ 'title': 'BREAKING CHANGE',
+ 'text': ''
+ };
+ var parent = ancestors.pop();
+ switch ( parent.type ) {
+ case 'summary':
+ breaking.text = out.subject;
+ break;
+ case 'body':
+ breaking.text = '';
+ visit( parent, [ 'text', 'breaking-change' ], visitBodyNode );
+ break;
+ case 'token':
+ parent = ancestors.pop();
+ visit( parent, 'text', visitTextNode );
+ break;
+ default:
+ break;
+ }
+ if ( breaking.text !== '' ) {
+ out.notes.push( breaking );
+ }
+
+ /**
+ * Callback invoked upon visiting a body node.
+ *
+ * @private
+ * @param {Object} node - AST node
+ */
+ function visitBodyNode( node ) {
+ if ( startCollecting && node.type === 'text' ) {
+ if ( breaking.text !== '' ) {
+ breaking.text += '\n';
+ }
+ breaking.text += node.value;
+ } else if ( node.type === 'breaking-change' ) {
+ startCollecting = true;
+ }
+ }
+
+ /**
+ * Callback invoked upon visiting a text node.
+ *
+ * @private
+ * @param {Object} node - AST node
+ */
+ function visitTextNode( node ) {
+ breaking.text = node.value;
+ }
+ }
+
+ /**
+ * Callback invoked upon visiting the body node.
+ *
+ * @private
+ * @param {Object} body - AST node
+ */
+ function onBody( body ) {
+ if ( body ) {
+ visit( body, 'text', extractBody );
+ }
+ }
+
+ /**
+ * Callback invoked upon visiting a text node.
+ *
+ * @private
+ * @param {Object} node - AST node
+ */
+ function extractBody( node ) {
+ if ( out.body !== '' ) {
+ out.body += '\n';
+ }
+ out.body += node.value;
+ }
+
+ /**
+ * Callback invoked upon visiting the summary node.
+ *
+ * @private
+ * @param {Object} summary - AST node
+ */
+ function onSummary( summary ) {
+ visit( summary, visitSummary );
+ }
+
+ /**
+ * Processes a summary node and extracts relevant information.
+ *
+ * @private
+ * @param {Object} node - AST node
+ */
+ function visitSummary( node ) {
+ switch ( node.type ) {
+ case 'type':
+ out.type = node.value;
+ out.header += node.value;
+ break;
+ case 'scope':
+ out.scope = node.value;
+ out.header += '(' + node.value + ')';
+ break;
+ case 'breaking-change':
+ out.header += '!';
+ break;
+ case 'text':
+ out.subject = node.value;
+ out.header += ': ' + node.value;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Processes a footer node and extracts relevant information.
+ *
+ * @private
+ * @param {Object} node - AST node
+ */
+ function processFooter( node ) {
+ var hasRefSeparator = false;
+ var coAuthoredBy = false;
+ var closesIssue = false;
+ var reference = {};
+ var hasPRURL = false;
+
+ visit( node, [ 'type', 'separator', 'text' ], processFooterNode );
+ if ( hasRefSeparator && RE_DIGITS.test( reference.ref ) ) {
+ reference.prefix = '#';
+ out.references.push( reference );
+ }
+ if ( hasPRURL || closesIssue || coAuthoredBy ) {
+ out.mentions.push( reference );
+ }
+
+ /**
+ * Processes a footer node and extracts relevant information.
+ *
+ * @private
+ * @param {Object} node - AST node
+ */
+ function processFooterNode( node ) {
+ switch ( node.type ) {
+ case 'type':
+ if ( node.value === 'PR-URL' ) {
+ hasPRURL = true;
+ } else if ( RE_CLOSES.test( node.value ) ) {
+ closesIssue = true;
+ } else if ( RE_CO_AUTHORED_BY.test( node.value ) ) {
+ coAuthoredBy = true;
+ }
+ reference.action = node.value;
+ break;
+ case 'separator':
+ if ( node.value.indexOf( '#' ) !== -1 ) {
+ hasRefSeparator = true;
+ }
+ break;
+ case 'text':
+ if ( node.value.indexOf( options.issueURL ) !== 1 ) {
+ reference.url = node.value;
+ reference.ref = node.value.split( '/' ).pop();
+ } else if ( node.value.indexOf( options.prURL ) !== 1 ) {
+ hasPRURL = true;
+ reference.url = node.value;
+ reference.ref = node.value.split('/').pop();
+ } else if ( node.value.charAt( 0 ) === '#' ) {
+ hasRefSeparator = true;
+ reference.ref = node.value.substring( 1 );
+ if ( !reference.url ) {
+ if ( closesIssue ) {
+ reference.url = options.issueURL + reference.ref;
+ } else if ( hasPRURL ) {
+ reference.url = options.prURL + reference.ref;
+ }
+ }
+ } else {
+ reference.ref = node.value;
+ if ( !reference.url ) {
+ if ( closesIssue ) {
+ reference.url = options.issueURL + reference.ref;
+ } else if ( hasPRURL ) {
+ reference.url = options.prURL + reference.ref;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+
+// EXPORTS //
+
+module.exports = toConventionalChangelog;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/defaults.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/defaults.js
new file mode 100644
index 00000000000..0783ec1d262
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/defaults.js
@@ -0,0 +1,39 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MAIN //
+
+/**
+* Returns default options.
+*
+* @private
+* @returns {Object} default options
+*/
+function defaults() {
+ return {
+ 'issueURL': 'https://github.com/stdlib-js/stdlib/issues/',
+ 'prURL': 'https://github.com/stdlib-js/stdlib/pull/'
+ };
+}
+
+
+// EXPORTS //
+
+module.exports = defaults;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/index.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/index.js
new file mode 100644
index 00000000000..70bce8a0a01
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/index.js
@@ -0,0 +1,42 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+/**
+* Parse package commit messages into conventional changelog objects.
+*
+* @module @stdlib/_tools/changelog/parse-commits
+*
+* @example
+* var join = require( 'path' ).join;
+* var rootDir = require( '@stdlib/_tools/utils/root-dir' );
+* var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' );
+*
+* var out = parseCommits( join( rootDir(), 'lib', 'node_modules', '@stdlib', 'utils', 'curry' ) );
+* // returns [...]
+*/
+
+// MODULES //
+
+var main = require( './main.js' );
+
+
+// EXPORTS //
+
+module.exports = main;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js
new file mode 100644
index 00000000000..8a2290d476d
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js
@@ -0,0 +1,104 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MODULES //
+
+var resolve = require( 'path' ).resolve;
+var logger = require( 'debug' );
+var parser = require( '@conventional-commits/parser' ).parser;
+var merge = require( '@stdlib/utils/merge' );
+var cwd = require( '@stdlib/process/cwd' );
+var toConventionalChangelog = require( './conventional_changelog.js' );
+var extractCommits = require( './commits.js' );
+var validate = require( './validate.js' );
+var defaults = require( './defaults.js' );
+
+
+// VARIABLES //
+
+var debug = logger( 'changelog:parse-commits' );
+
+
+// MAIN //
+
+/**
+* Parses package commit messages into conventional changelog objects.
+*
+* @param {Options} [options] - function options
+* @param {string} [options.dir] - root directory
+* @param {string} [options.issueURL] - issue URL
+* @param {string} [options.prURL] - PR URL
+* @throws {TypeError} options argument must be an object
+* @throws {TypeError} must provide valid options
+* @returns {Array} array of conventional changelog formatted commit message objects
+*
+* @example
+* var resolve = require( 'path' ).resolve;
+* var out = parseCommits({
+* 'dir': resolve( __dirname, '..', '..' )
+* });
+*/
+function parseCommits( options ) {
+ var commits;
+ var merged;
+ var opts;
+ var msg;
+ var ast;
+ var dir;
+ var err;
+ var out;
+ var i;
+ var j;
+
+ opts = defaults();
+ if ( arguments.length > 0 ) {
+ err = validate( opts, options );
+ if ( err ) {
+ throw err;
+ }
+ }
+ if ( opts.dir ) {
+ dir = resolve( cwd(), opts.dir );
+ } else {
+ dir = cwd();
+ }
+ commits = extractCommits( dir );
+ out = [];
+ for ( i = 0; i < commits.length; i++ ) {
+ msg = commits[ i ].message;
+ try {
+ ast = parser( msg );
+ } catch ( err ) {
+ debug( 'Encountered an error when parsing a commit message: %s. Error: %s', msg, err.message );
+ continue; // Skip this commit if parsing fails...
+ }
+ merged = merge( commits[ i ], toConventionalChangelog( ast, opts ) );
+ for ( j = 0; j < merged.notes.length; j++ ) {
+ merged.notes[ j ].hash = commits[ i ].hash;
+ }
+ out.push( merged );
+ }
+ return out;
+}
+
+
+// EXPORTS //
+
+module.exports = parseCommits;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/validate.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/validate.js
new file mode 100644
index 00000000000..e0ac9d12c67
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/validate.js
@@ -0,0 +1,70 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MODULES //
+
+var isObject = require( '@stdlib/assert/is-plain-object' );
+var isString = require( '@stdlib/assert/is-string' ).isPrimitive;
+var hasOwnProp = require( '@stdlib/assert/has-own-property' );
+var format = require( '@stdlib/string/format' );
+
+
+// MAIN //
+
+/**
+* Validates function options.
+*
+* @private
+* @param {Object} opts - destination for validated options
+* @param {Options} options - function options
+* @param {string} [options.dir] - root directory
+* @param {string} [options.issueURL] - issue URL
+* @param {string} [options.prURL] - PR URL
+* @returns {(null|Error)} null or an error
+*/
+function validate( opts, options ) {
+ if ( !isObject( options ) ) {
+ return new TypeError( format( 'invalid argument. Options argument must be an object. Value: `%s`.', options ) );
+ }
+ if ( hasOwnProp( options, 'dir' ) ) {
+ opts.dir = options.dir;
+ if ( !isString( opts.dir ) ) {
+ return new TypeError( format( 'invalid option. `%s` option must be a string. Option: `%s`.', 'dir', opts.dir ) );
+ }
+ }
+ if ( hasOwnProp( options, 'issueURL' ) ) {
+ opts.issueURL = options.issueURL;
+ if ( !isString( opts.issueURL ) ) {
+ return new TypeError( format( 'invalid option. `%s` option must be a string. Option: `%s`.', 'issueURL', opts.issueURL ) );
+ }
+ }
+ if ( hasOwnProp( options, 'prURL' ) ) {
+ opts.prURL = options.prURL;
+ if ( !isString( opts.prURL ) ) {
+ return new TypeError( format( 'invalid option. `%s` option must be a string. Option: `%s`.', 'prURL', opts.prURL ) );
+ }
+ }
+ return null;
+}
+
+
+// EXPORTS //
+
+module.exports = validate;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/package.json b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/package.json
new file mode 100644
index 00000000000..9b1f45cd5fd
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/package.json
@@ -0,0 +1,61 @@
+{
+ "name": "@stdlib/_tools/changelog/parse-commits",
+ "version": "0.0.0",
+ "description": "Parse commit messages into conventional changelog objects.",
+ "license": "Apache-2.0",
+ "author": {
+ "name": "The Stdlib Authors",
+ "url": "https://github.com/stdlib-js/stdlib/graphs/contributors"
+ },
+ "contributors": [
+ {
+ "name": "The Stdlib Authors",
+ "url": "https://github.com/stdlib-js/stdlib/graphs/contributors"
+ }
+ ],
+ "main": "./lib",
+ "directories": {
+ "example": "./examples",
+ "lib": "./lib",
+ "test": "./test"
+ },
+ "scripts": {},
+ "homepage": "https://github.com/stdlib-js/stdlib",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/stdlib-js/stdlib.git"
+ },
+ "bugs": {
+ "url": "https://github.com/stdlib-js/stdlib/issues"
+ },
+ "dependencies": {},
+ "devDependencies": {},
+ "engines": {
+ "node": ">=0.10.0",
+ "npm": ">2.7.0"
+ },
+ "os": [
+ "aix",
+ "darwin",
+ "freebsd",
+ "linux",
+ "macos",
+ "openbsd",
+ "sunos",
+ "win32",
+ "windows"
+ ],
+ "keywords": [
+ "stdlib",
+ "tools",
+ "tool",
+ "changelog",
+ "parse",
+ "parser",
+ "commits",
+ "conventional",
+ "metadata",
+ "git"
+ ],
+ "__stdlib__": {}
+}
diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/scripts/commits.sh b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/scripts/commits.sh
new file mode 100755
index 00000000000..c152f355481
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/scripts/commits.sh
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+#
+# @license Apache-2.0
+#
+# Copyright (c) 2024 The Stdlib Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Script for collecting commits for a given directory.
+#
+# Usage: commits.sh
+#
+# Arguments:
+#
+# dir Directory to search for commits.
+#
+# Environment variables:
+#
+# GIT_COMMIT_SEP: separator for separating commits in the output stream (default: '^---^')
+#
+
+if [ -z "$1" ]; then
+ echo "Error: must provide a directory to search for commits." >&2;
+ exit 1;
+fi
+
+if [ -z "$GIT_COMMIT_SEP" ]; then
+ GIT_COMMIT_SEP="^---^"; # Default separator
+fi
+
+git log --name-only --no-merges --pretty=format:"%H|%ad|%aN <%aE>|%B|" "$1" | awk -v sep="$GIT_COMMIT_SEP" '/^$/{p=1;next} /^[0-9a-f]{40}\|/{if (p==1) print sep; p=0} {print}';
diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js
new file mode 100644
index 00000000000..60a40669be7
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js
@@ -0,0 +1,117 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MODULES //
+
+var join = require( 'path' ).join;
+var tape = require( 'tape' );
+var rootDir = require( '@stdlib/_tools/utils/root-dir' );
+var isPlainObject = require( '@stdlib/assert/is-plain-object' );
+var isArray = require( '@stdlib/assert/is-array' );
+var hasOwnProp = require( '@stdlib/assert/has-own-property' );
+var parseCommits = require( './../lib' );
+
+
+// TESTS //
+
+tape( 'main export is a function', function test( t ) {
+ t.ok( true, __filename );
+ t.equal( typeof parseCommits, 'function', 'main export is a function' );
+ t.end();
+});
+
+tape( 'the function throws an error if provided an argument that is not an object', function test( t ) {
+ var values;
+ var i;
+
+ values = [
+ 'abc',
+ 5,
+ null,
+ true,
+ void 0,
+ NaN,
+ [],
+ function noop() {}
+ ];
+
+ for ( i = 0; i < values.length; i++ ) {
+ t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
+ }
+ t.end();
+
+ function badValue( value ) {
+ return function badValue() {
+ parseCommits( value );
+ };
+ }
+});
+
+tape( 'the function throws an error if provided an invalid option', function test( t ) {
+ t.throws( badValue, TypeError, 'throws error' );
+ t.end();
+
+ function badValue() {
+ var opts = {
+ 'dir': null
+ };
+ parseCommits( opts );
+ }
+});
+
+tape( 'the function returns an empty array if unable to resolve a directory or if unable to find any commits', function test( t ) {
+ var opts = {
+ 'dir': 'beepboop'
+ };
+ var out = parseCommits( opts );
+ t.strictEqual( isArray( out ), true, 'returns expected value' );
+ t.strictEqual( out.length, 0, 'returns expected value' );
+ t.end();
+});
+
+tape( 'the function returns an array of conventional changelog formatted commit message objects', function test( t ) {
+ var commits;
+ var commit;
+ var opts;
+ var i;
+
+ opts = {
+ 'dir': join( rootDir(), 'lib', 'node_modules', '@stdlib', 'utils', 'curry' )
+ };
+ commits = parseCommits( opts );
+ t.strictEqual( isArray( commits ), true, 'returns expected value' );
+ t.strictEqual( commits.length > 0, true, 'returns expected value' );
+ for ( i = 0; i < commits.length; i++ ) {
+ commit = commits[ i ];
+ t.strictEqual( isPlainObject( commit ), true, 'returns expected value' );
+ t.strictEqual( hasOwnProp( commit, 'hash' ), true, 'returns expected value' );
+ t.strictEqual( hasOwnProp( commit, 'date' ), true, 'returns expected value' );
+ t.strictEqual( hasOwnProp( commit, 'author' ), true, 'returns expected value' );
+ t.strictEqual( hasOwnProp( commit, 'message' ), true, 'returns expected value' );
+ t.strictEqual( hasOwnProp( commit, 'type' ), true, 'returns expected value' );
+ t.strictEqual( hasOwnProp( commit, 'scope' ), true, 'returns expected value' );
+ t.strictEqual( hasOwnProp( commit, 'subject' ), true, 'returns expected value' );
+ t.strictEqual( hasOwnProp( commit, 'body' ), true, 'returns expected value' );
+ t.strictEqual( hasOwnProp( commit, 'footer' ), true, 'returns expected value' );
+ t.strictEqual( hasOwnProp( commit, 'notes' ), true, 'returns expected value' );
+ t.strictEqual( hasOwnProp( commit, 'references' ), true, 'returns expected value' );
+ }
+ t.end();
+});
diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.validate.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.validate.js
new file mode 100644
index 00000000000..edec79b98f5
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.validate.js
@@ -0,0 +1,179 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MODULES //
+
+var tape = require( 'tape' );
+var validate = require( './../lib/validate.js' );
+
+
+// TESTS //
+
+tape( 'main export is a function', function test( t ) {
+ t.ok( true, __filename );
+ t.equal( typeof validate, 'function', 'main export is a function' );
+ t.end();
+});
+
+tape( 'if provided an `options` argument which is not an `object`, the function returns a type error', function test( t ) {
+ var values;
+ var opts;
+ var err;
+ var i;
+
+ values = [
+ '5',
+ 5,
+ NaN,
+ true,
+ null,
+ void 0,
+ [],
+ function noop() {}
+ ];
+
+ for ( i = 0; i < values.length; i++ ) {
+ opts = {};
+ err = validate( opts, values[i] );
+ t.equal( err instanceof TypeError, true, 'returns a type error when provided '+values[i] );
+ }
+ t.end();
+});
+
+tape( 'if provided a `dir` option which is not a `string`, the function returns a type error', function test( t ) {
+ var values;
+ var opts;
+ var err;
+ var i;
+
+ values = [
+ 5,
+ NaN,
+ true,
+ null,
+ void 0,
+ [],
+ {},
+ function noop() {}
+ ];
+
+ for ( i = 0; i < values.length; i++ ) {
+ opts = {};
+ err = validate( opts, {
+ 'dir': values[i]
+ });
+ t.equal( err instanceof TypeError, true, 'returns a type error when provided '+values[i] );
+ }
+ t.end();
+});
+
+tape( 'if provided an `issueURL` option which is not a `string`, the function returns a type error', function test( t ) {
+ var values;
+ var opts;
+ var err;
+ var i;
+
+ values = [
+ 5,
+ NaN,
+ true,
+ null,
+ void 0,
+ [],
+ {},
+ function noop() {}
+ ];
+
+ for ( i = 0; i < values.length; i++ ) {
+ opts = {};
+ err = validate( opts, {
+ 'prURL': values[i]
+ });
+ t.equal( err instanceof TypeError, true, 'returns a type error when provided '+values[i] );
+ }
+ t.end();
+});
+
+tape( 'if provided a `prURL` option which is not a `string`, the function returns a type error', function test( t ) {
+ var values;
+ var opts;
+ var err;
+ var i;
+
+ values = [
+ 5,
+ NaN,
+ true,
+ null,
+ void 0,
+ [],
+ {},
+ function noop() {}
+ ];
+
+ for ( i = 0; i < values.length; i++ ) {
+ opts = {};
+ err = validate( opts, {
+ 'prURL': values[i]
+ });
+ t.equal( err instanceof TypeError, true, 'returns a type error when provided '+values[i] );
+ }
+ t.end();
+});
+
+tape( 'the function returns `null` if all options are valid', function test( t ) {
+ var opts;
+ var obj;
+ var err;
+
+ opts = {
+ 'dir': '/foo/bar/baz',
+ 'issueURL': 'beep.com',
+ 'prURL': 'boop.com'
+ };
+ obj = {};
+ err = validate( obj, opts );
+
+ t.equal( err, null, 'returns null' );
+ t.strictEqual( obj.dir, opts.dir, 'sets dir option' );
+ t.strictEqual( obj.issueURL, opts.issueURL, 'sets issueURL option' );
+ t.strictEqual( obj.prURL, opts.prURL, 'sets prURL option' );
+
+ t.end();
+});
+
+tape( 'the function ignores unsupported/unrecognized options', function test( t ) {
+ var opts;
+ var obj;
+ var err;
+
+ opts = {
+ 'beep': 'boop',
+ 'a': 'b',
+ 'c': [ 1, 2, 3 ]
+ };
+ obj = {};
+ err = validate( obj, opts );
+
+ t.equal( err, null, 'returns null' );
+ t.deepEqual( obj, {}, 'does not set any options' );
+
+ t.end();
+});
diff --git a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/README.md b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/README.md
new file mode 100644
index 00000000000..d59f051ff7f
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/README.md
@@ -0,0 +1,118 @@
+
+
+# Recommend Version Bump
+
+> Recommend a version bump based on parsed commit messages.
+
+
+
+## Usage
+
+```javascript
+var recommendVersionBump = require( '@stdlib/_tools/changelog/recommend-version-bump' );
+```
+
+#### recommendVersionBump( commits )
+
+Recommends a version bump based on [parsed commit messages][@stdlib/_tools/changelog/parse-commits].
+
+```javascript
+var commits = [
+ {
+ 'type': 'fix',
+ 'notes': []
+ },
+ {
+ 'type': 'feat',
+ 'notes': [{
+ 'title': 'BREAKING CHANGE',
+ 'text': 'Changes in API'
+ }]
+ }
+];
+var version = recommendVersionBump( commits );
+// returns 'major'
+
+commits = [
+ {
+ 'type': 'feat',
+ 'notes': []
+ }
+];
+version = recommendVersionBump( commits );
+// returns 'minor'
+```
+
+The function returns a string indicating the recommended version bump or `null` if no version bump is recommended:
+
+- `'patch'`: a patch version bump (bug fixes).
+- `'minor'`: a minor version bump (addition of functionality in a backward compatible manner).
+- `'major'`: a major version bump (breaking changes, incompatible API changes).
+- `null`: no version bump recommended.
+
+
+
+
+
+
+
+
+
+
+
+## Examples
+
+```javascript
+var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' );
+var recommendVersionBump = require( '@stdlib/_tools/changelog/recommend-version-bump' );
+
+var commits = parseCommits();
+var version = recommendVersionBump( commits );
+// returns
+
+commits = [];
+version = recommendVersionBump( commits );
+// returns null
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+[@stdlib/_tools/changelog/parse-commits]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/_tools/changelog/parse-commits
+
+
+
+
diff --git a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/examples/index.js b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/examples/index.js
new file mode 100644
index 00000000000..f1b340fb97d
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/examples/index.js
@@ -0,0 +1,32 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' );
+var recommendVersionBump = require( './../lib' );
+
+var commits = parseCommits();
+var version = recommendVersionBump( commits );
+console.log( version );
+// =>
+
+commits = [];
+version = recommendVersionBump( commits );
+console.log( version );
+// => null
diff --git a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/index.js b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/index.js
new file mode 100644
index 00000000000..8620414e5f0
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/index.js
@@ -0,0 +1,57 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+/**
+* Recommend a version bump based on parsed commit messages.
+*
+* @module @stdlib/_tools/changelog/recommend-version-bump
+*
+* @example
+* var recommendVersionBump = require( '@stdlib/_tools/changelog/recommend-version-bump' );
+*
+* var commits = [
+* {
+* 'type': 'fix',
+* 'notes': []
+* },
+* {
+* 'type': 'feat',
+* 'notes': [{
+* 'title': 'BREAKING CHANGE',
+* 'text': 'Changes in API'
+* }]
+* }
+* ];
+* var version = recommendVersionBump( commits );
+* // returns 'major'
+*
+* commits = [];
+* version = recommendVersionBump( commits );
+* // returns null
+*/
+
+// MODULES //
+
+var main = require( './main.js' );
+
+
+// EXPORTS //
+
+module.exports = main;
diff --git a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/main.js
new file mode 100644
index 00000000000..d061dca3726
--- /dev/null
+++ b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/main.js
@@ -0,0 +1,61 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2024 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MAIN //
+
+/**
+* Recommends a version bump based on a list of commits.
+*
+* @param {Array