-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Pipedream Utils Components #15925
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pipedream Utils Components #15925
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎ 3 Skipped Deployments
|
WalkthroughThe pull request introduces an extensive set of new modules and actions to the Pipedream Utils app. These include utilities for date and time manipulation, text and JSON conversions, currency handling, RSS feed retrieval, file operations, email and S3 integrations, random generation, and workflow triggering and scheduling. Additionally, new common utilities for handling dates, numbers, and text operations have been added. The package configuration was updated with new dependencies and version bumps, and an obsolete app file was removed. Changes
Suggested labels
Suggested reviewers
Poem
Tip ⚡🧪 Multi-step agentic review comment chat (experimental)
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 49
🧹 Nitpick comments (78)
components/pipedream_utils/common/date-time/dateTimeUnits.mjs (1)
7-7: Consider accounting for leap years in the year calculationThe current implementation uses a simplified 365-day year. For more precise calculations, consider using 365.25 days to account for leap years, depending on your use case requirements.
-const year = 365 * day; +const year = 365.25 * day;components/pipedream_utils/actions/url-encode/url-encode.mjs (1)
4-4: Clarify description for URL encoding purposeThere's a slight inconsistency between the component description and the input description. The component description says "Encode a string as a URL" while the input description mentions "A valid URL as a string to be encoded".
- description: "Encode a string as a URL", + description: "URL encode a string for use in a URL",- description: "A valid URL as a string to be encoded.", + description: "String to be URL encoded (e.g., for query parameters, path segments).",Also applies to: 12-13
components/pipedream_utils/actions/get-current-time-in-specific-timezone/get-current-time-in-specific-timezone.mjs (1)
18-20: Consider adding a summary export for consistencyFor consistency with other actions like url-encode.mjs, consider adding a summary export to indicate successful execution.
async run() { - return moment.tz(new Date(), this.timezone).format(); + const result = moment.tz(new Date(), this.timezone).format(); + $.export("$summary", `Current time in ${this.timezone}: ${result}`); + return result; },components/pipedream_utils/actions/convert-html-to-slack-mrkdwn/convert-html-to-slack-mrkdwn.mjs (2)
8-8: Incomplete description sentenceThe description ends abruptly with "using" but doesn't specify what library/tool it's using.
- description: "Converts an HTML string to the Slack mrkdwn format using", + description: "Converts an HTML string to the Slack mrkdwn format using slackify-html",
18-20: Add summary export for consistent user experienceMost Pipedream actions export a summary to provide feedback to users. Consider adding one to maintain consistency with other components.
async run() { + const $ = this; + const result = slackifyHtml(this.html); + $.export("$summary", "Successfully converted HTML to Slack mrkdwn format"); - return slackifyHtml(this.html); + return result; },components/pipedream_utils/actions/date-time-format/date-time-format.mjs (2)
27-31: Add a comment explaining the dual formatting approachThe code uses both a custom mapping and the Sugar library for formatting. A comment would help maintainers understand why both approaches are used.
try { const response = DATE_FORMAT_PARSE_MAP.get(outputFormat); + // First check if we have a predefined format mapping, otherwise use Sugar's formatting const output = response ? response.outputFn(dateObj) : sugar.Date.format(dateObj, outputFormat);
35-37: Improve error message specificityThe current error message is generic. Consider including the original error message to help users troubleshoot more effectively.
catch (err) { - throw new ConfigurationError("**Parse error** - check your input and if the selected format is correct."); + throw new ConfigurationError(`**Parse error** - check your input and if the selected format is correct. Details: ${err.message}`); }components/pipedream_utils/actions/format-iso8601-datetime/format-iso8601-datetime.mjs (1)
2-2: Consider using a more modern date libraryMoment.js is now considered legacy by its maintainers. Consider using a more modern alternative like Day.js, date-fns, or Luxon for new code.
-import moment from "moment"; +import dayjs from "dayjs"; +import customParseFormat from "dayjs/plugin/customParseFormat.js"; +import utc from "dayjs/plugin/utc.js"; + +// Extend Day.js with plugins +dayjs.extend(customParseFormat); +dayjs.extend(utc);Then update the run method accordingly:
async run() { + // Validate that the provided string is a valid date + if (!dayjs(this.iso8601).isValid()) { + throw new Error(`Invalid ISO8601 date/time string: "${this.iso8601}"`); + } + return { - date_time: moment(this.iso8601).format("MM/DD/YYYY h:mm:ss a"), - date: moment(this.iso8601).format("MM/DD/YYYY"), - time: moment(this.iso8601).format("h:mm:ss a"), + date_time: dayjs(this.iso8601).format("MM/DD/YYYY h:mm:ss a"), + date: dayjs(this.iso8601).format("MM/DD/YYYY"), + time: dayjs(this.iso8601).format("h:mm:ss a"), }; },components/pipedream_utils/actions/random-integer/random-integer.mjs (2)
24-26: Make run method async for consistencyAll other action components use async run methods. For consistency, make this one async as well.
-run() { +async run() { return Math.floor(Math.random() * (this.max - this.min + 1) + this.min); },
24-26: Improve random integer generationThe current implementation works correctly but could be enhanced with additional error handling and a clearer return format.
-run() { +async run() { + // Handle the case where min equals max + if (this.min === this.max) { + return { + value: this.min, + min: this.min, + max: this.max + }; + } + + const randomValue = Math.floor(Math.random() * (this.max - this.min + 1) + this.min); + + return { + value: randomValue, + min: this.min, + max: this.max + }; - return Math.floor(Math.random() * (this.max - this.min + 1) + this.min); },components/pipedream_utils/actions/get-iso-string-n-days-ago/get-iso-string-n-days-ago.mjs (2)
18-21: Consider input validation and return valueThe component should validate that
this.daysis a valid number before using it with moment.js, and also consider returning the date value for better component usability.async run({ $ }) { + const days = parseInt(this.days); + if (isNaN(days)) { + throw new Error("Days must be a valid number"); + } - $.export("date", moment().subtract(this.days, "days") + const date = moment().subtract(days, "days") .toISOString()); + $.export("date", date); + $.export("$summary", `Generated ISO string for ${days} days ago`); + return { date }; },
2-2: Consider using dayjs instead of momentMoment.js is now considered legacy and its creators recommend using alternatives like dayjs for new projects.
-import moment from "moment"; +import dayjs from "dayjs";And update the implementation accordingly:
-$.export("date", moment().subtract(this.days, "days") - .toISOString()); +$.export("date", dayjs().subtract(this.days, "days") + .toISOString());components/pipedream_utils/common/text/splitTextOptions.mjs (2)
1-1: Document the significance of the INDEX_ALL_SEGMENTS valueThe value 999 seems arbitrary. Adding a comment would help explain why this specific value was chosen and how it's used.
+// A large number to represent "all segments" in text splitting operations export const INDEX_ALL_SEGMENTS = 999;
4-5: Track workaround for issue #5429The comment indicates a workaround for issue #5429. Consider adding a TODO to revisit this once the issue is fixed.
{ label: "First", - value: INDEX_ALL_SEGMENTS * -1, // value should be 0, but that is not accepted - see issue #5429 + value: INDEX_ALL_SEGMENTS * -1, // TODO: Change to 0 when issue #5429 is fixed },components/pipedream_utils/common/numbers/formatNumber.mjs (3)
1-1: Add function documentation and type validationThis utility function would benefit from clear documentation about its parameters and expected behavior, as well as validation for input types.
+/** + * Formats a number with customizable grouping and decimal separators + * @param {string} integer - The integer part of the number + * @param {string} [decimal] - The decimal part of the number (optional) + * @param {string} [groupChar=","] - Character used to separate groups of thousands + * @param {string} [decimalChar="."] - Character used as decimal separator + * @returns {string} The formatted number as a string + */ export default function (integer, decimal, groupChar = ",", decimalChar = ".") { + // Validate inputs + if (typeof integer !== 'string') { + throw new TypeError('Integer part must be a string'); + } + if (decimal !== undefined && typeof decimal !== 'string') { + throw new TypeError('Decimal part must be a string or undefined'); + } const result = [];
8-10: Simplify conditional logic and clarify reverse().pop() operationThe else clause is unnecessary since there's no early return in the if block. Also, the reverse().pop() operation could use a clarifying comment.
if (groupChar) { for (let i = integer.length; i > 0; i -= 3) { result.push(groupChar, integer.slice(Math.max(0, i - 3), i)); } + // Remove the leading separator after reversing the array result.reverse().pop(); } - else + else { result.push(integer); + }
3-7: Consider a more readable approach to grouping digitsThe current implementation works but is somewhat complex. A more readable approach using regular expressions could be considered.
- if (groupChar) { - for (let i = integer.length; i > 0; i -= 3) { - result.push(groupChar, integer.slice(Math.max(0, i - 3), i)); - } - result.reverse().pop(); + if (groupChar && integer.length > 3) { + // Add grouping separators every 3 digits from the right + result.push(integer.replace(/\B(?=(\d{3})+(?!\d))/g, groupChar)); }components/pipedream_utils/actions/convert-html-to-text/convert-html-to-text.mjs (3)
6-6: Align component key with naming patternThe component key doesn't follow the same pattern as other components in this PR. Consider using "pipedream_utils-convert-html-to-text" for consistency.
- key: "formatting-convert-html-to-text", + key: "pipedream_utils-convert-html-to-text",
17-21: Add error handling for the HTML conversionThe component should handle potential errors from the html-to-text library to provide a better user experience.
async run({ $ }) { const { input } = this; - const result = convert(input); - $.export("$summary", "Successfully converted to text"); - return result; + try { + const result = convert(input); + $.export("$summary", "Successfully converted to text"); + return result; + } catch (error) { + $.export("$summary", `Error converting HTML to text: ${error.message}`); + throw error; + } },
1-2: Consider adding options for HTML to text conversionThe html-to-text library supports various options to customize the conversion process, which could be exposed as props.
You could extend this component by adding optional configuration props like:
options: { label: "Conversion Options", description: "Advanced options for HTML to text conversion", type: "object", optional: true, }And then pass them to the convert function:
const result = convert(input, this.options || {});This would allow users to customize settings like wordwrap, link formatting, etc.
components/pipedream_utils/actions/format-number/format-number.mjs (1)
1-56: Comprehensive number formatting component with good error handlingThe component is well-structured with clear descriptions and proper error handling for multiple decimal marks. The formatting logic separates the integer and decimal parts, supporting various formatting styles.
A few suggestions to enhance robustness:
- Consider adding validation to ensure the input is actually a number before processing
- Add a try/catch block around the formatNumber call to handle any unexpected errors
async run({ $ }) { const { inputDecimalMark, toFormat, } = this; const input = this.input.toString(); + // Validate that input is a number + if (isNaN(Number(input))) { + throw new ConfigurationError("Input must be a valid number"); + } const decimalMark = inputDecimalMark ?? "."; const splitInput = input.split(decimalMark); if (splitInput.length > 2) { throw new ConfigurationError(`Input has more than one decimal mark (\`${decimalMark}\`). Check if the \`Input Decimal Mark\` prop is set correctly.`); } const [ integer, decimal, ] = splitInput; const [ groupChar, decimalChar, ] = toFormat.split(""); + try { const result = formatNumber(integer, decimal, groupChar, decimalChar); $.export("$summary", "Successfully formatted number"); return result; + } catch (error) { + throw new Error(`Error formatting number: ${error.message}`); + } },components/pipedream_utils/actions/get-time-in-specific-timezone/get-time-in-specific-timezone.mjs (1)
8-11: Consider adding a formatting option for more flexibilityCurrently, the component only returns the time in ISO format. Consider adding an optional format property to allow users to specify the desired output format.
export default { key: "pipedream_utils-get-time-in-specific-timezone", name: "Helper Functions - Get Time in Timezone", description: "Given an ISO 8601 timestamp, and a timezone, convert the time to the target timezone.", - version: "0.0.1", + version: "0.0.2", type: "action", props: { pipedream_utils, time: { type: "string", label: "ISO 8601 Time", description: "An [ISO 8601 string](https://en.wikipedia.org/wiki/ISO_8601) representing the time you'd like to convert to your target timezone. If this timestamp doesn't have a timezone component, it's assumed to be in UTC.", }, timezone: { type: "string", label: "Timezone", description: "The IANA timezone name, e.g. `America/Los_Angeles`. [See the full list here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).", }, + format: { + type: "string", + label: "Output Format", + description: "Optional. The format for the output time string. See [moment.js documentation](https://momentjs.com/docs/#/displaying/format/) for format options. Defaults to ISO 8601 format.", + optional: true, + }, }, async run({ $ }) { try { // Validate the timezone if (!moment.tz.zone(this.timezone)) { throw new Error(`Invalid timezone: ${this.timezone}. Please provide a valid IANA timezone name.`); } // Convert the time - const convertedTime = moment.tz(this.time, this.timezone).format(); + const time = moment.tz(this.time, this.timezone); + + // Check if the result is valid + if (!time.isValid()) { + throw new Error("Invalid date or time format. Please provide a valid ISO 8601 time string."); + } + + // Format the time + const convertedTime = this.format + ? time.format(this.format) + : time.format(); $.export("$summary", `Successfully converted time to ${this.timezone}`); return convertedTime; } catch (error) { throw new Error(`Error converting time: ${error.message}`); } }, };components/pipedream_utils/actions/schedule-task-in-future/schedule-task-in-future.mjs (2)
14-17: Consider using number type for numSecondsThe
numSecondsproperty is defined as a string type but is used in a mathematical calculation on line 34. While JavaScript will implicitly convert strings to numbers in mathematical operations, it would be clearer to define this as a number type or explicitly convert it when used.numSeconds: { - type: "string", + type: "integer", label: "Num Seconds", description: "How many seconds in the future would you like to schedule the task?", },
33-35: Consider refactoring timestamp calculation for better readabilityThe timestamp calculation is concise but a bit complex to read. Breaking it into steps would improve readability and make debugging easier.
- const ts = (new Date(+new Date() + (this.numSeconds * 1000))).toISOString(); + const currentTimeMs = Date.now(); + const futureTimeMs = currentTimeMs + (Number(this.numSeconds) * 1000); + const ts = new Date(futureTimeMs).toISOString(); $.export("ts", ts);components/pipedream_utils/actions/extract-email-address/extract-email-address.mjs (1)
19-21: Consider a more robust email regex patternThe current regex pattern for extracting email addresses is relatively simple and might not catch all valid email patterns or could match some invalid ones. Email validation is notoriously complex.
getRegExp() { - return /[\w.!#$%&'*+-/=?^_`{|}~]+@([\w-]+\.)+[\w-]{2,}/; + return /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+/; },components/pipedream_utils/actions/format-currency/format-currency.mjs (2)
43-52: Simplify decimal extraction logicThe current implementation for splitting the number into integer and decimal parts is unnecessarily complex. Consider using a more direct approach and add validation for the decimal part.
- const [ - integer, - decimal, - ] = input.split(input.includes(".") - ? "." - : ","); - if (isNaN(Number(integer))) { + // Parse the input using a regex that handles both dot and comma as decimal separators + const match = input.match(/^([^.,]+)(?:[.,]([^.,]*))?$/); + if (!match || isNaN(Number(match[1]))) { throw new ConfigurationError("**Invalid number** - please check your input."); } + const integer = match[1]; + const decimal = match[2] || "0";
52-56: Simplify conditional logic in formatNumber callThe nested ternary operations make the code harder to read. Consider restructuring this logic for better readability.
- const numberString = formatNumber(integer, (decimal?.length > 1 - ? decimal - : (decimal ?? "0") + "0"), currencyFormat.includes(",") - ? "," - : ""); + // Prepare the decimal part + let decimalPart = decimal || "0"; + if (decimalPart.length <= 1) { + decimalPart += "0"; + } + + // Determine separator + const separator = currencyFormat.includes(",") ? "," : ""; + + const numberString = formatNumber(integer, decimalPart, separator);components/pipedream_utils/actions/split-text/split-text.mjs (2)
42-43: Input validation missingThere's no validation to ensure
inputis a string before callingsplit(). Ifinputis undefined or null, this will cause an error.Add validation:
let summary = "Separator not found - returned unmodified input"; let result = input; + if (typeof input !== 'string') { + $.export("$summary", "Error: Input must be a string"); + return input; + } const arrResults = input.split(separator);
56-60: Inconsistent indentation and potential out-of-bounds accessThe indentation here is inconsistent, and there's no check for out-of-bounds indices when accessing
arrResults.Fix indentation and add a boundary check:
default: - result = - arrResults[segmentIndex < 0 - ? length + segmentIndex - : segmentIndex]; + const index = segmentIndex < 0 ? length + segmentIndex : segmentIndex; + result = (index >= 0 && index < length) ? arrResults[index] : null; + if (result === null) { + summary += `, but index ${segmentIndex} is out of bounds`; + } break;components/pipedream_utils/common/text/commonExtractText.mjs (3)
10-12: Add error handling and input validationThe
getResultmethod doesn't handle potential errors whengetRegExp()returns an invalid regular expression or wheninputisn't a string.getResult(input) { + if (typeof input !== 'string') { + return null; + } + try { return input.match(this.getRegExp())?.[0]; + } catch (error) { + console.error("Error in regex matching:", error); + return null; + } },
17-24: Add error handling in run methodThe
runmethod should handle potential errors fromgetResultandgetType.async run({ $ }) { const input = this.input; + try { const result = this.getResult(input); + const type = this.getType(); $.export("$summary", result - ? `Successfully found ${this.getType()} "${result}"` - : `No ${this.getType()} found`); + ? `Successfully found ${type} "${result}"` + : `No ${type} found`); return result; + } catch (error) { + $.export("$summary", `Error: ${error.message}`); + return null; + } },
6-16: Consider caching RegExp for performanceCreating regular expressions repeatedly can impact performance. Consider caching the RegExp object.
methods: { + _cachedRegExp: null, getRegExp() { throw new Error("RegExp not implemented for this action!"); }, + getCachedRegExp() { + if (!this._cachedRegExp) { + this._cachedRegExp = this.getRegExp(); + } + return this._cachedRegExp; + }, getResult(input) { - return input.match(this.getRegExp())?.[0]; + return input.match(this.getCachedRegExp())?.[0]; }, getType() { throw new Error("Type not implemented for this action!"); }, },components/pipedream_utils/README.md (1)
1-15: Expand README with more comprehensive contentThe README provides a good overview but could be enhanced with more detailed information about the available components, installation instructions, and usage examples.
Consider adding:
- A table of contents
- List of available components grouped by category (text, date, etc.)
- Basic usage instructions with code examples
- Links to more detailed documentation
Also, as noted by the static analysis tool, you could strengthen the wording in line 3 by replacing "speeding up" with "accelerating" or "enhancing".
🧰 Tools
🪛 LanguageTool
[style] ~3-~3: Consider using a different verb to strengthen your wording.
Context: ...ate code needed for routine operations, speeding up the development of intricate automation...(SPEED_UP_ACCELERATE)
components/pipedream_utils/actions/extract-number/extract-number.mjs (2)
5-8: Add more detail to the component descriptionThe current description mentions that it finds the first number only, but doesn't specify what qualifies as a number (e.g., whether it handles negative numbers, scientific notation, etc.).
name: "Formatting - [Text] Extract Number", -description: "Find a number out of a text field. Finds the first number only.", +description: "Find a number out of a text field. Finds the first number only. Supports positive and negative numbers, decimals, and scientific notation.", key: "formatting-extract-number",
2-26: Consider returning actual number valuesThe component currently returns the matched number as a string. Users might expect an actual numeric value.
Add an option to convert the result to a number:
props: { ...commonExtractText.props, input: { label: "Input", description: "String from which you'd like to extract a number", type: "string", }, + convertToNumber: { + label: "Convert to Number", + description: "Convert the result to a numeric value", + type: "boolean", + default: false, + }, }, methods: { ...commonExtractText.methods, getRegExp() { return /[0-9][0-9.,]*/; }, getType() { return "number"; }, + getResult(input) { + const result = commonExtractText.methods.getResult.call(this, input); + if (result && this.convertToNumber) { + return Number(result); + } + return result; + }, },components/pipedream_utils/common/numbers/currencies.mjs (1)
1-773: Consider optimizing currency data accessFor better performance when working with currencies, consider adding utility functions to find currencies by ISO code, symbol, or name.
const CURRENCIES = [ // ... existing currency data ]; export const CURRENCY_OPTIONS = CURRENCIES.map(({ name, symbol, iso, }) => ({ label: name, value: `${iso} - ${symbol} - ${name}`, })); + // Utility functions for finding currencies + export const findCurrencyByISO = (isoCode) => + CURRENCIES.find(currency => currency.iso === isoCode); + + export const getCurrencySymbol = (isoCode) => { + const currency = findCurrencyByISO(isoCode); + return currency ? currency.symbol : null; + };components/pipedream_utils/actions/convert-markdown-to-html/convert-markdown-to-html.mjs (2)
18-25: Add error handling to the run methodThe current implementation lacks error handling. If showdown encounters problems with malformed Markdown or if other issues arise during conversion, the component will throw an unhandled exception.
async run({ $ }) { const { input } = this; - const converter = new showdown.Converter(); - const dom = parseHTML(""); - const result = converter.makeHtml(input, dom.window.document); - $.export("$summary", "Successfully converted to HTML"); - return result; + try { + if (typeof input !== 'string') { + throw new Error('Input must be a string'); + } + const converter = new showdown.Converter(); + const dom = parseHTML(""); + const result = converter.makeHtml(input, dom.window.document); + $.export("$summary", "Successfully converted to HTML"); + return result; + } catch (error) { + $.export("$summary", `Failed to convert Markdown to HTML: ${error.message}`); + throw error; + } },
12-16: Consider adding input validation or examplesThe
inputproperty could benefit from default examples or additional validation to ensure users provide proper Markdown. This would improve the usability of the component.input: { label: "Input", description: "Markdown string to be converted to HTML", type: "string", + default: "# Hello World\n\nThis is **Markdown**.", },components/pipedream_utils/actions/retrieve-all-rss-stories/retrieve-all-rss-stories.mjs (1)
9-10: Consider bumping the version numberThe component uses version "0.0.1" which suggests it's in a very early stage. If the component is ready for production use, consider bumping the version number to at least "0.1.0".
description: "Retrieve all stories from one or more RSS feeds.", - version: "0.0.1", + version: "0.1.0", type: "action",components/pipedream_utils/actions/send-to-s3/send-to-s3.mjs (2)
22-25: Consider adding additional S3 configuration optionsThe component allows only basic configuration for S3 uploads. Consider adding options for content-type, ACL, and other S3-specific settings.
payload: { type: "object", description: "An object, either a reference to a variable from a previous step (for example, event.body), or a set of hardcoded key-value pairs.", }, + contentType: { + type: "string", + label: "Content Type", + description: "The MIME content type of the object being uploaded", + optional: true, + }, + acl: { + type: "string", + label: "ACL", + description: "The access control list for the object being uploaded", + optional: true, + options: [ + { label: "Private", value: "private" }, + { label: "Public Read", value: "public-read" }, + { label: "Public Read-Write", value: "public-read-write" }, + ], + },
8-9: Consider bumping the version numberThe component uses version "0.0.1" which suggests it's in a very early stage. If the component is ready for production use, consider bumping the version number to at least "0.1.0".
description: "Send data to Amazon S3 using Pipedream's destination integration. See https://docs.pipedream.com/destinations/s3/", - version: "0.0.1", + version: "0.1.0", type: "action",components/pipedream_utils/common/text/caseOptions.mjs (3)
7-12: Improve the Capitalize function to handle edge casesThe current capitalization function doesn't handle empty words or strings with multiple spaces correctly. It will also throw an error if a word is an empty string (due to accessing index 0).
outputFn(str) { return str .split(" ") - .map((word) => word[0].toUpperCase() + word.slice(1)) + .map((word) => word.length ? word[0].toUpperCase() + word.slice(1) : "") .join(" "); },
4-41: Add input validation to prevent errors with non-string inputsNone of the transformation functions validate their input. This could lead to errors if the input is not a string or is null/undefined.
Consider adding a helper function for input validation, for example:
const validateInput = (str) => { if (typeof str !== 'string') { throw new Error('Input must be a string'); } return str; };Then update each outputFn to use this validation:
outputFn(str) { + str = validateInput(str); return str .split(" ") .map((word) => word.length ? word[0].toUpperCase() + word.slice(1) : "") .join(" "); },Apply similar changes to all other transformation functions.
43-48: Add a comment explaining the purpose of CASE_OPTIONS_PROPThe purpose of
CASE_OPTIONS_PROPis not immediately clear. It would be helpful to add a comment explaining its usage.+// Extract only the label and value properties from CASE_OPTIONS +// This is used for generating UI option lists without the implementation functions export const CASE_OPTIONS_PROP = CASE_OPTIONS.map(({ label, value, }) => ({ label, value, }));components/pipedream_utils/actions/compare-arrays/compare-arrays.mjs (3)
6-6: Fix typo in descriptionThere is a typo in the description: "symetric" should be "symmetric" to match the option in line 31.
- description: "Get the difference, intersection, union, or symetric difference of two arrays/sets.", + description: "Get the difference, intersection, union, or symmetric difference of two arrays/sets.",
36-40: Consider preserving the original SetThe
getDifferencemethod creates a new array from set1 and then filters it, which modifies neither original Set. However, the implementation is inconsistent with other methods likegetUnionandgetSymmetricDifferencewhich do modify the original Set.getDifference(set1, set2) { - return new Set([ - ...set1, - ].filter((x) => !set2.has(x))); + const result = new Set(); + for (const elem of set1) { + if (!set2.has(elem)) { + result.add(elem); + } + } + return result; },
41-45: Consider preserving the original SetSimilar to the previous comment, the
getIntersectionmethod creates a new array, filters it, and then returns a new Set, which doesn't modify either original Set.getIntersection(set1, set2) { - return new Set([ - ...set1, - ].filter((x) => set2.has(x))); + const result = new Set(); + for (const elem of set1) { + if (set2.has(elem)) { + result.add(elem); + } + } + return result; },components/pipedream_utils/actions/parse-json/parse-json.mjs (2)
2-7: Inconsistent key naming patternThe key
formatting-parse-jsondoesn't follow the pattern in other components which use the app name as prefix (e.g.,pipedream_utils-compare-arrays).- key: "formatting-parse-json", + key: "pipedream_utils-parse-json",
16-25: Consider adding input validation and improved error handlingThe error handling is good, but you might want to add validation for empty input and provide more specific error messages.
async run({ $ }) { + if (!this.input || !this.input.trim()) { + throw new Error("Input cannot be empty or whitespace"); + } try { const result = JSON.parse(this.input); $.export("$summary", "Successfully parsed JSON string"); return result; } catch (err) { - throw new Error("Error parsing input as JSON: " + err.toString()); + throw new Error(`Error parsing input as JSON: ${err.message || err.toString()}`); } },components/pipedream_utils/actions/compare-dates/compare-dates.mjs (3)
4-11: Inconsistent key naming patternSimilar to the previous file, the key
formatting-compare-datesdoesn't follow the naming pattern used in other components likepipedream_utils-compare-arrays.- key: "formatting-compare-dates", + key: "pipedream_utils-compare-dates",
36-56: Improve readability of date duration formattingThe date difference calculation is complex and could benefit from better variable naming and comments to improve readability.
Consider extracting the duration formatting logic into a separate method for better readability and maintainability. This would also make the
runmethod cleaner.formatDuration(milliseconds) { if (!milliseconds) return "equal"; const arrResults = []; const arrUnits = Object.entries(DATE_TIME_UNITS).sort((a, b) => b[1] - a[1]); let remaining = milliseconds; for (const [unitName, unitValue] of arrUnits) { const amount = Math.floor(remaining / unitValue); if (amount) { arrResults.push(`${amount} ${amount === 1 ? unitName : `${unitName}s`}`); remaining %= unitValue; if (!remaining) break; } } return arrResults.join(", "); }
41-54: Improve variable namingThe variable names could be more descriptive to better reflect their purpose.
- for (const [ - word, - unit, - ] of arrUnits) { - const amount = Math.floor(remainingValue / unit); + for (const [ + unitName, + unitValue, + ] of arrUnits) { + const amount = Math.floor(remainingValue / unitValue); if (amount) { arrResults.push(`${amount} ${amount === 1 - ? word - : `${word}s`}`); - remainingValue %= unit; + ? unitName + : `${unitName}s`}`); + remainingValue %= unitValue;components/pipedream_utils/actions/set-default-value/set-default-value.mjs (2)
2-7: Inconsistent key naming patternSimilar to the other files, the key
formatting-set-default-valuedoesn't follow the pattern used in components likepipedream_utils-compare-arrays.- key: "formatting-set-default-value", + key: "pipedream_utils-set-default-value",
21-30: Handle broader range of empty valuesThe current implementation only checks for falsy values, which may not catch all cases of "empty" input, such as strings with only whitespace.
async run({ $ }) { const { input, defaultValue, } = this; - const result = input || defaultValue; + const result = (input === undefined || input === null || input === '') + ? defaultValue + : input; $.export("$summary", input - ? "Checked text - not empty" - : "Replaced empty text with default value"); + ? "Input is not empty - using original value" + : "Input is empty - using default value"); return result; },components/pipedream_utils/actions/extract-phone-number/extract-phone-number.mjs (1)
9-31: Consider expanding phone number format optionsThe current regex patterns may not catch all common phone number formats. The NANP regex won't match plain 10 digits (e.g., "1234567890") or formats with dots (e.g., "123.456.7890"). The international format is quite restrictive and won't match many valid international phone numbers.
Consider adding more formats or a more comprehensive regex pattern. For example:
options: [ { label: "North American Number Plan (NANP) e.g. `(123) 456-7890` or `123-456-7890`", value: "((\\([0-9]{3}\\) ?)|[0-9]{3}-)[0-9]{3}-[0-9]{4}", }, { label: "International e.g. `(12) 34-56-78-90`", value: "\\([0-9]{2}\\) ?([0-9]{2}-){3}[0-9]{2}", }, + { + label: "Generic (matches most formats)", + value: "\\+?[0-9]{1,4}?[-. ]?\\(?[0-9]{1,3}?\\)?[-. ]?[0-9]{1,4}[-. ]?[0-9]{1,4}[-. ]?[0-9]{1,9}", + }, ],components/pipedream_utils/actions/convert-json-to-string/convert-json-to-string.mjs (1)
16-25: Consider adding formatting options and handling circular referencesThe current implementation is straightforward but could be enhanced with options for pretty-printing JSON and handling circular references.
Consider adding these improvements:
props: { pipedream_utils, input: { label: "Input", description: "An object to be serialized to a JSON format string.", type: "object", }, + indentation: { + label: "Indentation", + description: "Number of spaces to use for indentation (0 for no formatting)", + type: "integer", + default: 0, + optional: true, + }, }, async run({ $ }) { try { - const result = JSON.stringify(this.input); + const result = JSON.stringify(this.input, function(key, value) { + // Handle circular references + if (typeof value === 'object' && value !== null) { + if (seen.has(value)) { + return '[Circular Reference]'; + } + seen.add(value); + } + return value; + }, this.indentation || null); + seen = null; // Clean up $.export("$summary", "Successfully convert object to JSON string"); return result; }Also, consider adding a
seen = new WeakSet()variable before the try block to support the circular reference detection.components/pipedream_utils/actions/download-file-to-tmp/download-file-to-tmp.mjs (1)
42-47: Consider returning a more structured and informative response.The current return value is an array with filename and path. A structured object would be more self-documenting and could include additional useful metadata.
- const filedata = [ - filename, - downloadedFilepath, - ]; - - return filedata; + return { + filename, + path: downloadedFilepath, + size: fs.statSync(downloadedFilepath).size, + url: url, + };components/pipedream_utils/actions/send-email-with-nodemailer/send-email-with-nodemailer.mjs (2)
89-96: Consider adding support for more email features.The current implementation is missing support for common email features like BCC, attachments, and reply-to headers.
var mailOptions = { from: this.from, to: this.to, cc: this.cc, + bcc: this.bcc, + replyTo: this.replyTo, subject: this.subject, text: (this.text || ""), html: (this.html || ""), + attachments: this.attachments, };Don't forget to add the corresponding props in the props section:
bcc: { type: "string", description: "Comma separated list or an array of recipients email addresses that will appear on the Bcc: field", optional: true, }, replyTo: { type: "string", description: "Email address that will appear on the Reply-To: field", optional: true, }, attachments: { type: "object", description: "An array of attachment objects (see Nodemailer docs)", optional: true, },
1-1: Remove legacy_hash_id comment if not needed.The comment with
legacy_hash_idappears to be an artifact from a previous system or migration.-// legacy_hash_id: a_EViLg3components/pipedream_utils/actions/convert-object-to-json-string/convert-object-to-json-string.mjs (1)
1-1: Remove legacy_hash_id comment if not needed.The comment with
legacy_hash_idappears to be an artifact from a previous system or migration.-// legacy_hash_id: a_a4i80Ocomponents/pipedream_utils/package.json (1)
1-27: Consider adding additional package.json metadata.For better package documentation and maintenance, consider adding:
- A
repositoryfield pointing to the GitHub repository- A
bugsfield for issue reporting- A
licensefield (if applicable)- An
enginesfield to specify Node.js version compatibilityThis is particularly important for utility packages that may be used across different projects.
components/pipedream_utils/actions/get-coutry-name-by-code-iso/get-coutry-name-by-code-iso.mjs (2)
18-265: Move country data outside the run methodThe country code mapping is defined inside the
runmethod, which means it's recreated every time the action runs. For better performance, move this large object outside the method as a constant.import pipedream_utils from "../../pipedream_utils.app.mjs"; +const ISO_COUNTRIES = { + "AF": "Afghanistan", + "AX": "Aland Islands", + // ... rest of the country definitions + "ZW": "Zimbabwe", +}; + export default { key: "pipedream_utils-get-country-name-by-code-iso", // ... rest of component definition async run() { const { countryCode } = this; - const isoCountries = { - "AF": "Afghanistan", - "AX": "Aland Islands", - // ... rest of the country definitions - "ZW": "Zimbabwe", - }; - if (isoCountries.get(countryCode)) { - return isoCountries[countryCode]; + if (ISO_COUNTRIES[countryCode]) { + return ISO_COUNTRIES[countryCode]; } else { return countryCode; } }, };
17-271: Add input normalization for country codesThe component doesn't normalize the input country code, which could lead to failed lookups if users provide lowercase codes.
async run() { const { countryCode } = this; + // Normalize the country code to uppercase + const normalizedCode = countryCode ? countryCode.toUpperCase() : countryCode; // ... rest of the function - if (isoCountries[countryCode]) { - return isoCountries[countryCode]; + if (ISO_COUNTRIES[normalizedCode]) { + return ISO_COUNTRIES[normalizedCode]; } else { - return countryCode; + return normalizedCode; } },components/pipedream_utils/actions/transform-case/transform-case.mjs (2)
26-39: Enhance error message with specific failure detailsThe current error handling throws a generic configuration error. Consider enhancing it with more specific information about what went wrong.
try { const { outputFn } = CASE_OPTIONS.find(({ value }) => value === operation); const result = outputFn(input); $.export("$summary", "Successfully transformed text case"); return result; } catch (err) { - throw new ConfigurationError("**Parse error** - check your input and if the selected operation is correct."); + throw new ConfigurationError(`**Parse error** - ${err.message}. Please check your input and ensure the selected operation (${operation}) is correct.`); }
30-33: Add validation for invalid operation selectionThe code doesn't explicitly check if a valid operation was found in CASE_OPTIONS. If
operationdoesn't match any value,outputFnwill be undefined, causing an error.try { - const { outputFn } = CASE_OPTIONS.find(({ value }) => value === operation); + const option = CASE_OPTIONS.find(({ value }) => value === operation); + if (!option) { + throw new Error(`Invalid operation: ${operation}`); + } + const { outputFn } = option; const result = outputFn(input); $.export("$summary", "Successfully transformed text case"); return result; }components/pipedream_utils/common/date-time/commonDateTime.mjs (1)
24-39: Consider adding timezone handling capabilitiesThe date parsing functionality works well, but doesn't explicitly handle timezone information. For a more robust solution, consider adding timezone support to avoid unexpected date/time conversions.
The current implementation will use the local timezone when parsing dates without explicit timezone information. For international applications, you might want to:
- Allow users to specify a timezone
- Clarify in documentation how timezones are handled
- Consider using a library like date-fns-tz or moment-timezone for more robust timezone support
components/pipedream_utils/actions/retrieve-new-rss-stories/retrieve-new-rss-stories.mjs (1)
42-42: Add export summary for better workflow visibilityAdding a summary export will make it clearer to users what the action accomplished.
this.db.set("$checkpoint", previouslyPostedStories.concat(newStories.map((s) => s.link))); + $.export("$summary", `Found ${newStories.length} new stories from RSS feeds`); return newStories;components/pipedream_utils/actions/add-subtract-time/add-subtract-time.mjs (3)
42-65: Improve the duration parsing logic to handle more formatsThe current implementation only supports basic formats like "1s", "1m", etc., but doesn't handle more complex formats like "1.5h" or combinations like "1h30m".
getOperationMilliseconds(str) { - let result = 0; - const { - second, minute, hour, day, week, year, - } = DATE_TIME_UNITS; - Object.entries({ - s: second, - m: minute, - h: hour, - d: day, - w: week, - y: year, - }).forEach(([ - identifier, - multiplier, - ]) => { - const substr = str.match(new RegExp(`[0-9]+\\s*${identifier}`))?.[0]; - if (substr) { - const value = Number(substr.match(/[0-9]+/)); - result += value * multiplier; - } - }); - return result; + const normalizedStr = str.toLowerCase().trim(); + let result = 0; + const { + second, minute, hour, day, week, year, + } = DATE_TIME_UNITS; + + // Handle combined formats (e.g., "1h30m") + const patterns = [ + { regex: /([0-9]+\.?[0-9]*)[ ]*s(ec(ond)?s?)?/g, multiplier: second }, + { regex: /([0-9]+\.?[0-9]*)[ ]*m(in(ute)?s?)?/g, multiplier: minute }, + { regex: /([0-9]+\.?[0-9]*)[ ]*h(r|our)?s?/g, multiplier: hour }, + { regex: /([0-9]+\.?[0-9]*)[ ]*d(ay)?s?/g, multiplier: day }, + { regex: /([0-9]+\.?[0-9]*)[ ]*w(eek)?s?/g, multiplier: week }, + { regex: /([0-9]+\.?[0-9]*)[ ]*y(ear)?s?/g, multiplier: year }, + ]; + + patterns.forEach(({ regex, multiplier }) => { + let match; + while ((match = regex.exec(normalizedStr)) !== null) { + const value = parseFloat(match[1]); + if (!isNaN(value)) { + result += value * multiplier; + } + } + }); + + return result; },
78-89: Improve error message with more specific informationThe current error message doesn't provide enough information to help users debug their issue.
try { const { outputFn } = DATE_FORMAT_PARSE_MAP.get(format); const output = outputFn(sugar.Date.create(result)); $.export("$summary", `Successfully ${operation === OPERATION_OPTIONS.SUBTRACT ? "subtracted" : "added"} time`); return output; } catch (err) { console.log("Error parsing date", err); - throw new ConfigurationError("**Parse error** - check your input and if the selected format is correct."); + throw new ConfigurationError(`**Parse error** - Unable to format the date using the '${format}' format. Please check your input date and selected format. Original error: ${err.message}`); }
81-83: Simplify the ternary operator for better readabilityThe current ternary operator spans multiple lines and could be simplified.
- $.export("$summary", `Successfully ${operation === OPERATION_OPTIONS.SUBTRACT - ? "subtracted" - : "added"} time`); + const actionWord = operation === OPERATION_OPTIONS.SUBTRACT ? "subtracted" : "added"; + $.export("$summary", `Successfully ${actionWord} time`);components/pipedream_utils/actions/export-variables/export-variables.mjs (2)
25-35: Enhance the parse method to handle more edge casesThe current implementation doesn't handle all potential edge cases, which could lead to unexpected errors.
parse(value) { const valueToParse = this.emptyStrToUndefined(value); - if (typeof(valueToParse) === "object" || valueToParse === undefined) { + // Return null, undefined, or already object values as is + if (valueToParse === null || valueToParse === undefined || typeof(valueToParse) === "object") { return valueToParse; } + // Handle non-string primitives + if (typeof(valueToParse) !== "string") { + return valueToParse; + } try { return JSON.parse(valueToParse); } catch (e) { - throw new ConfigurationError("Make sure the custom expression contains a valid object"); + throw new ConfigurationError(`Failed to parse value as JSON: ${e.message}. Make sure the custom expression contains a valid object.`); } },
37-39: Add success feedback and error handling to the run methodThe current implementation doesn't provide any feedback or error handling.
run({ $ }) { - $.export("config", this.parse(this.config)); + try { + const parsedConfig = this.parse(this.config); + $.export("config", parsedConfig); + $.export("$summary", `Successfully exported ${Object.keys(parsedConfig || {}).length} configuration variable(s)`); + } catch (error) { + console.error("Error parsing configuration:", error); + throw error; + } },components/pipedream_utils/actions/csv-file-to-objects/csv-file-to-objects.mjs (2)
57-63: Consider using asynchronous file operations.The current implementation uses
readFileSyncwhich blocks the event loop. For better performance and scalability, especially with large files, consider using the asynchronousfs.promises.readFileinstead.-import { readFileSync } from "fs"; +import { promises as fsPromises } from "fs"; // ... - fileContent = readFileSync(path.resolve(filePath), "utf8"); + fileContent = await fsPromises.readFile(path.resolve(filePath), "utf8");
73-74: Add more detailed summary information.The current summary only indicates the number of records converted. Consider providing more details such as the CSV file name to make the summary more informative.
- $.export("$summary", `Converted ${records.length} records from CSV to objects.`); + const fileName = path.basename(filePath); + $.export("$summary", `Converted ${records.length} records from CSV file "${fileName}" to objects.`);components/pipedream_utils/actions/replace-text/replace-text.mjs (2)
40-56: Simplify text replacement logic.The current implementation uses a potentially inefficient loop for non-regex replacements. Modern JavaScript provides the
replaceAllmethod which would be more efficient. While the comment mentions Node 14.x compatibility, consider detecting ifreplaceAllis available:- // replaceAll would be optimal, but it is not supported in Node 14.x - let result = input.replace(expression, replaceValue); - if (!isRegExp) { - let counter = 0; - while (result.match(expression)) { - result = result.replace(expression, replaceValue); - if (++counter > MAX_REPLACES) { - throw new Error(`Over ${MAX_REPLACES} replace operations reached - please check your inputs and, if this is expected, search with a regular expression instead of text`); - } - } - } + let result; + if (isRegExp) { + result = input.replace(expression, replaceValue); + } else if (typeof input.replaceAll === 'function') { + // Use replaceAll if available (Node.js 15.0.0+) + result = input.replaceAll(expression, replaceValue); + } else { + // Fallback for older Node.js versions + result = input.replace(expression, replaceValue); + let counter = 0; + while (result.match(expression)) { + result = result.replace(expression, replaceValue); + if (++counter > MAX_REPLACES) { + throw new Error(`Over ${MAX_REPLACES} replace operations reached - please check your inputs and, if this is expected, search with a regular expression instead of text`); + } + } + }
51-56: Improve match count calculation.The current implementation for counting matches has a potential issue. For non-regex replacements, it's only checking if the input includes the findText, and then setting matchText to "text" rather than the actual count. Consider improving this to provide an accurate count:
- const matchText = isRegExp - ? [ - ...input.matchAll(expression), - ].length - : input.includes(findText) && "text"; - $.export("$summary", matchText - ? `Successfully replaced ${matchText} matches` - : "No matches found. Input was not modified"); + let matchCount = 0; + if (isRegExp) { + matchCount = [...input.matchAll(expression)].length; + } else { + // Count occurrences for non-regex + let tempInput = input; + let index = tempInput.indexOf(findText); + while (index !== -1) { + matchCount++; + tempInput = tempInput.substring(index + findText.length); + index = tempInput.indexOf(findText); + } + } + + $.export("$summary", matchCount > 0 + ? `Successfully replaced ${matchCount} ${matchCount === 1 ? 'match' : 'matches'}` + : "No matches found. Input was not modified");components/pipedream_utils/common/date-time/dateFormats.mjs (1)
167-168: Add comprehensive documentation for exported constants.Consider adding JSDoc comments to the exported constants to better document their purpose, expected usage, and any constraints:
+/** + * Maps date format strings to their corresponding input and output functions. + * Input functions convert strings to date objects, while output functions format date objects as strings. + * @type {Map<string, {inputFn?: Function, outputFn: Function}>} + */ export const DATE_FORMAT_PARSE_MAP = new Map(mapData); +/** + * Array of all supported date format strings. + * These can be used as options for date format selection in the UI. + * @type {string[]} + */ export const DATE_FORMAT_OPTIONS = DATE_FORMATS.map(({ value }) => value);components/pipedream_utils/actions/convert-currency/convert-currency.mjs (1)
34-48: Consider caching currency options.The
getCurrencyOptionsmethod fetches the currency list from an external API each time it's called. Since currency lists don't change frequently, consider implementing caching to reduce API calls:+// Cache to store currency options +let currencyOptionsCache = null; +let cacheTime = null; +const CACHE_DURATION = 60 * 60 * 1000; // 1 hour in milliseconds methods: { async getCurrencyOptions() { + // Use cached options if they exist and are not expired + const now = Date.now(); + if (currencyOptionsCache && cacheTime && now - cacheTime < CACHE_DURATION) { + return currencyOptionsCache; + } const currencies = await this.getCurrencies(); const options = []; for (const [ key, value, ] of Object.entries(currencies)) { options.push({ value: key, label: value, }); } + + // Update cache + currencyOptionsCache = options; + cacheTime = now; return options; },
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (61)
components/pipedream_utils/README.md(1 hunks)components/pipedream_utils/actions/add-subtract-time/add-subtract-time.mjs(1 hunks)components/pipedream_utils/actions/base64-decode-string/base64-decode-string.mjs(1 hunks)components/pipedream_utils/actions/compare-arrays/compare-arrays.mjs(1 hunks)components/pipedream_utils/actions/compare-dates/compare-dates.mjs(1 hunks)components/pipedream_utils/actions/convert-currency/convert-currency.mjs(1 hunks)components/pipedream_utils/actions/convert-html-to-markdown/convert-html-to-markdown.mjs(1 hunks)components/pipedream_utils/actions/convert-html-to-slack-mrkdwn/convert-html-to-slack-mrkdwn.mjs(1 hunks)components/pipedream_utils/actions/convert-html-to-text/convert-html-to-text.mjs(1 hunks)components/pipedream_utils/actions/convert-json-to-string/convert-json-to-string.mjs(1 hunks)components/pipedream_utils/actions/convert-markdown-to-html/convert-markdown-to-html.mjs(1 hunks)components/pipedream_utils/actions/convert-object-to-json-string/convert-object-to-json-string.mjs(1 hunks)components/pipedream_utils/actions/csv-file-to-objects/csv-file-to-objects.mjs(1 hunks)components/pipedream_utils/actions/date-time-format/date-time-format.mjs(1 hunks)components/pipedream_utils/actions/download-file-to-tmp/download-file-to-tmp.mjs(1 hunks)components/pipedream_utils/actions/export-variables/export-variables.mjs(1 hunks)components/pipedream_utils/actions/extract-by-regular-expression/extract-by-regular-expression.mjs(1 hunks)components/pipedream_utils/actions/extract-email-address/extract-email-address.mjs(1 hunks)components/pipedream_utils/actions/extract-number/extract-number.mjs(1 hunks)components/pipedream_utils/actions/extract-phone-number/extract-phone-number.mjs(1 hunks)components/pipedream_utils/actions/extract-url/extract-url.mjs(1 hunks)components/pipedream_utils/actions/format-currency/format-currency.mjs(1 hunks)components/pipedream_utils/actions/format-iso8601-datetime/format-iso8601-datetime.mjs(1 hunks)components/pipedream_utils/actions/format-number/format-number.mjs(1 hunks)components/pipedream_utils/actions/get-coutry-name-by-code-iso/get-coutry-name-by-code-iso.mjs(1 hunks)components/pipedream_utils/actions/get-current-time-in-specific-timezone/get-current-time-in-specific-timezone.mjs(1 hunks)components/pipedream_utils/actions/get-iso-string-n-days-ago/get-iso-string-n-days-ago.mjs(1 hunks)components/pipedream_utils/actions/get-time-in-specific-timezone/get-time-in-specific-timezone.mjs(1 hunks)components/pipedream_utils/actions/html-to-markdown/html-to-markdown.mjs(1 hunks)components/pipedream_utils/actions/parse-json/parse-json.mjs(1 hunks)components/pipedream_utils/actions/prettify-json/prettify-json.mjs(1 hunks)components/pipedream_utils/actions/random-integer/random-integer.mjs(1 hunks)components/pipedream_utils/actions/random-item-from-list/random-item-from-list.mjs(1 hunks)components/pipedream_utils/actions/replace-text/replace-text.mjs(1 hunks)components/pipedream_utils/actions/retrieve-all-rss-stories/retrieve-all-rss-stories.mjs(1 hunks)components/pipedream_utils/actions/retrieve-new-rss-stories/retrieve-new-rss-stories.mjs(1 hunks)components/pipedream_utils/actions/schedule-task-in-future/schedule-task-in-future.mjs(1 hunks)components/pipedream_utils/actions/send-email-with-nodemailer/send-email-with-nodemailer.mjs(1 hunks)components/pipedream_utils/actions/send-to-s3/send-to-s3.mjs(1 hunks)components/pipedream_utils/actions/set-default-value/set-default-value.mjs(1 hunks)components/pipedream_utils/actions/split-text/split-text.mjs(1 hunks)components/pipedream_utils/actions/transform-case/transform-case.mjs(1 hunks)components/pipedream_utils/actions/trigger-workflow/trigger-workflow.mjs(1 hunks)components/pipedream_utils/actions/trim-whitespace/trim-whitespace.mjs(1 hunks)components/pipedream_utils/actions/url-decode/url-decode.mjs(1 hunks)components/pipedream_utils/actions/url-encode/url-encode.mjs(1 hunks)components/pipedream_utils/actions/xml-to-json/xml-to-json.mjs(1 hunks)components/pipedream_utils/common/date-time/commonDateTime.mjs(1 hunks)components/pipedream_utils/common/date-time/dateFormats.mjs(1 hunks)components/pipedream_utils/common/date-time/dateTimeUnits.mjs(1 hunks)components/pipedream_utils/common/numbers/currencies.mjs(1 hunks)components/pipedream_utils/common/numbers/currencyFormats.mjs(1 hunks)components/pipedream_utils/common/numbers/formatNumber.mjs(1 hunks)components/pipedream_utils/common/numbers/numberFormattingOptions.mjs(1 hunks)components/pipedream_utils/common/text/buildRegExp.mjs(1 hunks)components/pipedream_utils/common/text/caseOptions.mjs(1 hunks)components/pipedream_utils/common/text/commonExtractText.mjs(1 hunks)components/pipedream_utils/common/text/splitTextOptions.mjs(1 hunks)components/pipedream_utils/package.json(2 hunks)components/pipedream_utils/pipedream_utils.app.mjs(1 hunks)components/pipedream_utils/pipedream_utils.mjs(0 hunks)
💤 Files with no reviewable changes (1)
- components/pipedream_utils/pipedream_utils.mjs
🧰 Additional context used
🪛 GitHub Check: Lint Code Base
components/pipedream_utils/actions/convert-html-to-slack-mrkdwn/convert-html-to-slack-mrkdwn.mjs
[warning] 13-13:
Component prop html must have a description. See https://pipedream.com/docs/components/guidelines/#props
components/pipedream_utils/actions/format-iso8601-datetime/format-iso8601-datetime.mjs
[warning] 13-13:
Component prop iso8601 must have a description. See https://pipedream.com/docs/components/guidelines/#props
components/pipedream_utils/actions/convert-object-to-json-string/convert-object-to-json-string.mjs
[warning] 12-12:
Component prop object must have a label. See https://pipedream.com/docs/components/guidelines/#props
components/pipedream_utils/actions/get-time-in-specific-timezone/get-time-in-specific-timezone.mjs
[warning] 18-18:
Component prop timezone must have a label. See https://pipedream.com/docs/components/guidelines/#props
components/pipedream_utils/actions/html-to-markdown/html-to-markdown.mjs
[warning] 13-13:
Component prop input must have a description. See https://pipedream.com/docs/components/guidelines/#props
components/pipedream_utils/actions/get-current-time-in-specific-timezone/get-current-time-in-specific-timezone.mjs
[warning] 13-13:
Component prop timezone must have a label. See https://pipedream.com/docs/components/guidelines/#props
components/pipedream_utils/actions/get-iso-string-n-days-ago/get-iso-string-n-days-ago.mjs
[warning] 13-13:
Component prop days must have a description. See https://pipedream.com/docs/components/guidelines/#props
components/pipedream_utils/actions/get-coutry-name-by-code-iso/get-coutry-name-by-code-iso.mjs
[warning] 12-12:
Component prop countryCode must have a label. See https://pipedream.com/docs/components/guidelines/#props
components/pipedream_utils/actions/base64-decode-string/base64-decode-string.mjs
[warning] 12-12:
Component prop data must have a label. See https://pipedream.com/docs/components/guidelines/#props
[warning] 12-12:
Component prop data must have a description. See https://pipedream.com/docs/components/guidelines/#props
🪛 LanguageTool
components/pipedream_utils/README.md
[style] ~3-~3: Consider using a different verb to strengthen your wording.
Context: ...ate code needed for routine operations, speeding up the development of intricate automation...
(SPEED_UP_ACCELERATE)
🪛 Biome (1.9.4)
components/pipedream_utils/actions/format-currency/format-currency.mjs
[error] 59-61: The default clause should be the last switch clause.
The following case clause is here:
Regardless its position, the default clause is always executed when there is no match. To avoid confusion, the default clause should be the last switch clause.
(lint/suspicious/useDefaultSwitchClauseLast)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: pnpm publish
- GitHub Check: Verify TypeScript components
- GitHub Check: Publish TypeScript components
🔇 Additional comments (32)
components/pipedream_utils/common/date-time/dateTimeUnits.mjs (1)
1-16: Simple, effective time unit definitionsThe constants are well-organized and each unit is defined in terms of the preceding unit, which is a clean approach. This makes the code maintainable and easy to understand.
components/pipedream_utils/actions/url-encode/url-encode.mjs (1)
1-21: Well-structured URL encoding componentThe component is properly structured with clear naming, good descriptions, and appropriate implementation using the standard
encodeURIComponentfunction.components/pipedream_utils/actions/trim-whitespace/trim-whitespace.mjs (1)
1-22: LGTM: Straightforward and well-implemented trim whitespace componentThis component correctly implements a text whitespace trimming utility with appropriate props and descriptions. The implementation is clean and performs the exact functionality described.
components/pipedream_utils/common/numbers/currencyFormats.mjs (1)
1-26: LGTM: Well-structured currency format optionsThe currency format options are clearly defined with helpful example labels and appropriate ICU format patterns for different display preferences.
components/pipedream_utils/common/numbers/numberFormattingOptions.mjs (1)
1-28: Well-structured formatting options constantsThe constants provide clear, descriptive options for decimal marks and number formatting patterns. The consistent structure with label/value pairs makes them easy to use in dropdown menus.
components/pipedream_utils/actions/schedule-task-in-future/schedule-task-in-future.mjs (2)
40-42: LGTM: Secure handling of optional secretGood implementation of conditional header inclusion. This ensures the secret is only included when provided, maintaining security best practices.
44-51: LGTM: Well-structured HTTP requestThe axios request is well structured with proper URL composition, headers, and payload data. It correctly sends the timestamp and message to the task scheduler endpoint.
components/pipedream_utils/actions/random-item-from-list/random-item-from-list.mjs (1)
27-29: LGTM: Good validation for quantity parameterThe validation ensures that the quantity requested doesn't exceed the list size, throwing an appropriate configuration error with a descriptive message.
components/pipedream_utils/actions/extract-email-address/extract-email-address.mjs (1)
6-8:✅ Verification successful
Check key and naming consistency
The key is "formatting-extract-email-address" but it's part of the pipedream_utils components. Ensure this naming convention is consistent with other components in the library.
🏁 Script executed:
#!/bin/bash # Check the naming patterns for other components to ensure consistency echo "Checking key naming patterns in other components..." grep -r "key:" --include="*.mjs" components/pipedream_utils/actions/ | head -n 10Length of output: 1438
Naming Consistency Verified for
extract-email-addressThe key
"formatting-extract-email-address"is consistent with the naming pattern used by other formatting-related actions in thepipedream_utilslibrary (e.g.,"formatting-add-subtract-time","formatting-compare-dates","formatting-convert-html-to-markdown", etc.). No changes are necessary.components/pipedream_utils/actions/split-text/split-text.mjs (1)
50-52:✅ Verification successful
Implementation comment that should be addressed
There's a comment about an issue (#5429) that suggests this case wouldn't be needed if 0 was accepted as an option.
🏁 Script executed:
#!/bin/bash # Check the current status of issue #5429 gh issue view 5429 || echo "Issue not found or not accessible"Length of output: 1284
Action Required: Clarify Temporary Workaround for Issue #5429
After verifying that issue #5429 is still open and the bug (where integer props with options do not accept 0) persists, the current implementation in
components/pipedream_utils/actions/split-text/split-text.mjs(lines 50-52) remains necessary. Please consider updating the comment to clearly mark this case as a temporary workaround and add a TODO note, such as:
- "TODO: Remove this case once integer props with options accept 0 (see #5429)."
This will help future developers understand the intent and know to revisit this section once the bug is fixed.
components/pipedream_utils/common/text/commonExtractText.mjs (1)
7-9: Good use of abstract method patternThe method throws an appropriate error to indicate it should be implemented by extending modules.
components/pipedream_utils/actions/html-to-markdown/html-to-markdown.mjs (2)
5-10: Action metadata looks goodThe component is well-structured with appropriate key, name, description, version, and type properties.
102-117: Run method implementation is solidThe implementation correctly initializes the TurndownService with all the user-configurable options and returns the converted markdown content.
components/pipedream_utils/actions/xml-to-json/xml-to-json.mjs (3)
4-9: Well-structured component metadataThe component has clear and descriptive metadata with appropriate key, name, description, version, and type.
11-30: Props are well-documentedAll props have descriptive labels and helpful descriptions, including links to documentation for additional information. Default values are appropriately set for optional properties.
32-39: Run method properly implements XML to JSON conversionThe implementation correctly uses the xml2js function with the user-provided options and returns the converted JSON. The summary export provides good feedback to the user.
components/pipedream_utils/common/numbers/currencies.mjs (2)
1-767: Comprehensive currency data implementationThe CURRENCIES array provides a thorough collection of world currencies with consistent structure (name, symbol, ISO code). This will be valuable for various currency-related operations.
768-773: Effective currency options mappingThe exported CURRENCY_OPTIONS transforms the raw currency data into a format suitable for dropdown menus or selectors, with a descriptive label and value format that includes all relevant information.
components/pipedream_utils/actions/compare-arrays/compare-arrays.mjs (1)
63-91: Improve run method with non-mutating operationsIf you implement the suggested changes to create new Sets rather than modifying the originals, the run method would still work correctly, but would be more consistent and safer.
The switch statement is well-structured and handles errors appropriately. The final conversion from Set to Array is correct.
components/pipedream_utils/actions/extract-phone-number/extract-phone-number.mjs (3)
1-4: LGTM: Good use of extending the common moduleThe component properly extends the
commonExtractTextmodule, which promotes code reuse and consistency across text extraction components.
4-8: Clear naming and metadataThe name, description, key, version, and type are well-defined and follow a consistent naming pattern that clearly identifies this as a formatting utility for text.
32-40: LGTM: Methods implementation is clean and straightforwardThe
getRegExpandgetTypemethods are implemented correctly and concisely. ThegetRegExpmethod properly creates a new RegExp object based on the selected format.components/pipedream_utils/actions/extract-url/extract-url.mjs (2)
1-8: LGTM: Component structure and metadataThe component follows the same pattern as other extract components, with clear naming and metadata.
9-16: Props defined correctlyThe input property is well-defined with appropriate label, description, and type.
components/pipedream_utils/actions/convert-json-to-string/convert-json-to-string.mjs (1)
1-15: LGTM: Component definition and propsThe component properly defines its metadata and props with clear labels and descriptions.
components/pipedream_utils/actions/trigger-workflow/trigger-workflow.mjs (1)
1-22: LGTM: Well-defined component structure and propsThe component has clear metadata and well-defined props with descriptive labels and helpful descriptions.
components/pipedream_utils/actions/download-file-to-tmp/download-file-to-tmp.mjs (1)
29-32: Good use of axios with arraybuffer response type.The configuration correctly specifies
responseType: "arraybuffer"which is appropriate for downloading binary files.components/pipedream_utils/package.json (2)
3-3: Version bump is appropriate for new features.The version increase from 0.0.1 to 0.0.2 is appropriate for adding new functionality.
15-25: Dependencies are well-structured with appropriate version constraints.The added dependencies all use caret ranges (
^) which is good practice as it allows for compatible minor and patch updates while maintaining the major version.components/pipedream_utils/common/date-time/commonDateTime.mjs (2)
9-15: LGTM! Good use of description for inputDateThe description provides clear guidance on how the date string will be processed, including information about the fallback behavior when a format isn't specified.
30-31: Good validation for invalid datesI appreciate the check for invalid dates using
isNaN(dateObj.getFullYear())- this is a reliable way to detect invalid Date objects.components/pipedream_utils/pipedream_utils.app.mjs (1)
6-26: LGTM: Well-structured prop definitions for date handlingThe prop definitions for
inputDate,inputFormat, andoutputFormatare well-structured with clear labels, descriptions, and appropriate options. This provides a good foundation for date manipulation utilities throughout the application.
| timezone: { | ||
| type: "string", | ||
| description: "The IANA timezone name, e.g. `America/Los_Angeles`. [See the full list here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).", | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add label to timezone property
The static analysis detected that the timezone prop is missing a required label property.
timezone: {
type: "string",
+ label: "Timezone",
description: "The IANA timezone name, e.g. `America/Los_Angeles`. [See the full list here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).",
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| timezone: { | |
| type: "string", | |
| description: "The IANA timezone name, e.g. `America/Los_Angeles`. [See the full list here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).", | |
| }, | |
| timezone: { | |
| type: "string", | |
| label: "Timezone", | |
| description: "The IANA timezone name, e.g. `America/Los_Angeles`. [See the full list here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).", | |
| }, |
🧰 Tools
🪛 GitHub Check: Lint Code Base
[warning] 13-13:
Component prop timezone must have a label. See https://pipedream.com/docs/components/guidelines/#props
| }, | ||
| }, | ||
| async run() { | ||
| set(this, "prettifiedValue.val", JSON.stringify(this.val, null, 2)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Parse JSON input before prettifying
The current implementation doesn't parse the input string as JSON before prettifying. If the input is already a JSON string, this will add unnecessary quotes.
- set(this, "prettifiedValue.val", JSON.stringify(this.val, null, 2));
+ try {
+ const parsed = JSON.parse(this.val);
+ set(this, "prettifiedValue.val", JSON.stringify(parsed, null, 2));
+ } catch (error) {
+ throw new Error("Invalid JSON input: " + error.message);
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| set(this, "prettifiedValue.val", JSON.stringify(this.val, null, 2)); | |
| try { | |
| const parsed = JSON.parse(this.val); | |
| set(this, "prettifiedValue.val", JSON.stringify(parsed, null, 2)); | |
| } catch (error) { | |
| throw new Error("Invalid JSON input: " + error.message); | |
| } |
| async run() { | ||
| set(this, "prettifiedValue.val", JSON.stringify(this.val, null, 2)); | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Function doesn't return the prettified JSON
The run method sets a property on this but doesn't return a value. This is inconsistent with typical action patterns where the result is returned directly.
async run() {
- set(this, "prettifiedValue.val", JSON.stringify(this.val, null, 2));
+ const prettified = JSON.stringify(this.val, null, 2);
+ $.export("$summary", "Successfully prettified JSON");
+ return prettified;
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async run() { | |
| set(this, "prettifiedValue.val", JSON.stringify(this.val, null, 2)); | |
| }, | |
| async run() { | |
| const prettified = JSON.stringify(this.val, null, 2); | |
| $.export("$summary", "Successfully prettified JSON"); | |
| return prettified; | |
| }, |
| html: { | ||
| type: "string", | ||
| label: "HTML", | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing description for HTML prop
The static analysis correctly flagged that the HTML prop is missing a description, which is required by Pipedream's documentation guidelines.
html: {
type: "string",
label: "HTML",
+ description: "The HTML content to convert to Slack mrkdwn format",
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| html: { | |
| type: "string", | |
| label: "HTML", | |
| }, | |
| html: { | |
| type: "string", | |
| label: "HTML", | |
| description: "The HTML content to convert to Slack mrkdwn format", | |
| }, |
🧰 Tools
🪛 GitHub Check: Lint Code Base
[warning] 13-13:
Component prop html must have a description. See https://pipedream.com/docs/components/guidelines/#props
| export default function (str, defaultFlags) { | ||
| const end = str.match(/\/[a-z]*$/); | ||
| if (!end) { | ||
| throw new ConfigurationError("Parse error - invalid regular expression."); | ||
| } | ||
| str = str.slice(1, end[0].length * -1); | ||
| let flags = end[0].split("/")[1] ?? ""; | ||
| defaultFlags?.forEach((flag) => { | ||
| if (!flags.includes(flag)) | ||
| flags += flag; | ||
| }); | ||
| return new RegExp(str, flags); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve regex extraction and error handling
The regex extraction logic has a few issues:
- The function assumes the regex always starts with '/' but doesn't check for it
- The pattern extraction logic on line 7 is problematic - it uses
end[0].length * -1which could incorrectly slice the string if there are escaped forward slashes in the pattern
-export default function (str, defaultFlags) {
+/**
+ * Builds a RegExp object from a string representation
+ * @param {string} str - The string representation of regex (e.g., "/pattern/flags")
+ * @param {string[]} [defaultFlags] - Optional default flags to add if not present
+ * @returns {RegExp} - The constructed RegExp object
+ * @throws {ConfigurationError} - If the string is not a valid regex
+ */
+export default function (str, defaultFlags) {
const end = str.match(/\/[a-z]*$/);
if (!end) {
- throw new ConfigurationError("Parse error - invalid regular expression.");
+ throw new ConfigurationError("Parse error - invalid regular expression. Format should be '/pattern/flags'");
}
- str = str.slice(1, end[0].length * -1);
- let flags = end[0].split("/")[1] ?? "";
+
+ // Find the position of the last slash
+ const lastSlashPos = str.lastIndexOf('/');
+ if (lastSlashPos <= 0) {
+ throw new ConfigurationError("Parse error - invalid regular expression. Missing delimiters.");
+ }
+
+ // Extract pattern (everything between the first and last slash)
+ const pattern = str.slice(1, lastSlashPos);
+
+ // Extract flags (everything after the last slash)
+ let flags = str.slice(lastSlashPos + 1) ?? "";
defaultFlags?.forEach((flag) => {
if (!flags.includes(flag))
flags += flag;
});
- return new RegExp(str, flags);
+
+ try {
+ return new RegExp(pattern, flags);
+ } catch (error) {
+ throw new ConfigurationError(`Invalid regular expression: ${error.message}`);
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export default function (str, defaultFlags) { | |
| const end = str.match(/\/[a-z]*$/); | |
| if (!end) { | |
| throw new ConfigurationError("Parse error - invalid regular expression."); | |
| } | |
| str = str.slice(1, end[0].length * -1); | |
| let flags = end[0].split("/")[1] ?? ""; | |
| defaultFlags?.forEach((flag) => { | |
| if (!flags.includes(flag)) | |
| flags += flag; | |
| }); | |
| return new RegExp(str, flags); | |
| } | |
| /** | |
| * Builds a RegExp object from a string representation | |
| * @param {string} str - The string representation of regex (e.g., "/pattern/flags") | |
| * @param {string[]} [defaultFlags] - Optional default flags to add if not present | |
| * @returns {RegExp} - The constructed RegExp object | |
| * @throws {ConfigurationError} - If the string is not a valid regex | |
| */ | |
| export default function (str, defaultFlags) { | |
| const end = str.match(/\/[a-z]*$/); | |
| if (!end) { | |
| throw new ConfigurationError("Parse error - invalid regular expression. Format should be '/pattern/flags'"); | |
| } | |
| // Find the position of the last slash | |
| const lastSlashPos = str.lastIndexOf('/'); | |
| if (lastSlashPos <= 0) { | |
| throw new ConfigurationError("Parse error - invalid regular expression. Missing delimiters."); | |
| } | |
| // Extract pattern (everything between the first and last slash) | |
| const pattern = str.slice(1, lastSlashPos); | |
| // Extract flags (everything after the last slash) | |
| let flags = str.slice(lastSlashPos + 1) ?? ""; | |
| defaultFlags?.forEach((flag) => { | |
| if (!flags.includes(flag)) | |
| flags += flag; | |
| }); | |
| try { | |
| return new RegExp(pattern, flags); | |
| } catch (error) { | |
| throw new ConfigurationError(`Invalid regular expression: ${error.message}`); | |
| } | |
| } |
| export const DEFAULT_INPUT_FUNCTION = (str) => { | ||
| const num = Number(str); | ||
| return sugar.Date.create(num * 1000 || str); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve error handling in the DEFAULT_INPUT_FUNCTION.
The current implementation doesn't handle potential errors when parsing dates. If the input is invalid, it might return an unexpected date object. Consider adding validation and explicit error handling:
export const DEFAULT_INPUT_FUNCTION = (str) => {
const num = Number(str);
- return sugar.Date.create(num * 1000 || str);
+ if (!str) {
+ throw new Error('Date string is required');
+ }
+
+ const dateObj = sugar.Date.create(num * 1000 || str);
+
+ // Validate that we got a valid date
+ if (isNaN(dateObj.getTime())) {
+ throw new Error(`Invalid date format: ${str}`);
+ }
+
+ return dateObj;
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const DEFAULT_INPUT_FUNCTION = (str) => { | |
| const num = Number(str); | |
| return sugar.Date.create(num * 1000 || str); | |
| }; | |
| export const DEFAULT_INPUT_FUNCTION = (str) => { | |
| const num = Number(str); | |
| if (!str) { | |
| throw new Error('Date string is required'); | |
| } | |
| const dateObj = sugar.Date.create(num * 1000 || str); | |
| // Validate that we got a valid date | |
| if (isNaN(dateObj.getTime())) { | |
| throw new Error(`Invalid date format: ${str}`); | |
| } | |
| return dateObj; | |
| }; |
| { | ||
| value: "22-01-2006", | ||
| inputFn(str) { | ||
| const [ | ||
| day, | ||
| month, | ||
| year, | ||
| ] = str.split("-"); | ||
| return sugar.Date.create(`${year}-${month}-${day}`, { | ||
| fromUTC: true, | ||
| }); | ||
| }, | ||
| outputFn(dateObj) { | ||
| return sugar.Date.format(dateObj, "%d-%m-%Y"); | ||
| }, | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add safeguards against invalid date components.
In the inputFn for the "22-01-2006" format, there's no validation that the split operation produced the expected components. This could lead to unexpected behavior or errors if the input doesn't match the expected format:
inputFn(str) {
const [
day,
month,
year,
] = str.split("-");
+
+ // Validate that we have all expected components
+ if (!day || !month || !year) {
+ throw new Error(`Invalid date format: ${str}. Expected format: DD-MM-YYYY`);
+ }
+
+ // Validate that components are numbers
+ if (isNaN(Number(day)) || isNaN(Number(month)) || isNaN(Number(year))) {
+ throw new Error(`Invalid date components in: ${str}. Day, month, and year must be numbers`);
+ }
return sugar.Date.create(`${year}-${month}-${day}`, {
fromUTC: true,
});
}Similar validation should be added to other inputFn implementations.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| { | |
| value: "22-01-2006", | |
| inputFn(str) { | |
| const [ | |
| day, | |
| month, | |
| year, | |
| ] = str.split("-"); | |
| return sugar.Date.create(`${year}-${month}-${day}`, { | |
| fromUTC: true, | |
| }); | |
| }, | |
| outputFn(dateObj) { | |
| return sugar.Date.format(dateObj, "%d-%m-%Y"); | |
| }, | |
| }, | |
| { | |
| value: "22-01-2006", | |
| inputFn(str) { | |
| const [ | |
| day, | |
| month, | |
| year, | |
| ] = str.split("-"); | |
| // Validate that we have all expected components | |
| if (!day || !month || !year) { | |
| throw new Error(`Invalid date format: ${str}. Expected format: DD-MM-YYYY`); | |
| } | |
| // Validate that components are numbers | |
| if (isNaN(Number(day)) || isNaN(Number(month)) || isNaN(Number(year))) { | |
| throw new Error(`Invalid date components in: ${str}. Day, month, and year must be numbers`); | |
| } | |
| return sugar.Date.create(`${year}-${month}-${day}`, { | |
| fromUTC: true, | |
| }); | |
| }, | |
| outputFn(dateObj) { | |
| return sugar.Date.format(dateObj, "%d-%m-%Y"); | |
| }, | |
| }, |
| const response = await this.convertCurrency($); | ||
| $.export("$summary", `${this.value} ${this.fromCurrency} = ${response.rates[this.toCurrency]} ${this.toCurrency}`); | ||
| return response; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add validation for API response.
The current implementation assumes the API response will contain the expected data structure. Consider adding validation to ensure the response contains the required properties:
async run({ $ }) {
const response = await this.convertCurrency($);
+
+ // Validate response contains expected data
+ if (!response || !response.rates || !response.rates[this.toCurrency]) {
+ throw new Error(`Invalid API response: Missing conversion rate for ${this.toCurrency}`);
+ }
$.export("$summary", `${this.value} ${this.fromCurrency} = ${response.rates[this.toCurrency]} ${this.toCurrency}`);
return response;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const response = await this.convertCurrency($); | |
| $.export("$summary", `${this.value} ${this.fromCurrency} = ${response.rates[this.toCurrency]} ${this.toCurrency}`); | |
| return response; | |
| async run({ $ }) { | |
| const response = await this.convertCurrency($); | |
| // Validate response contains expected data | |
| if (!response || !response.rates || !response.rates[this.toCurrency]) { | |
| throw new Error(`Invalid API response: Missing conversion rate for ${this.toCurrency}`); | |
| } | |
| $.export("$summary", `${this.value} ${this.fromCurrency} = ${response.rates[this.toCurrency]} ${this.toCurrency}`); | |
| return response; | |
| } |
| getCurrencies($ = this) { | ||
| return axios($, { | ||
| url: "https://api.frankfurter.app/currencies", | ||
| }); | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add error handling for API requests.
The current implementation of getCurrencies doesn't handle potential API failures. Consider adding error handling:
getCurrencies($ = this) {
- return axios($, {
- url: "https://api.frankfurter.app/currencies",
- });
+ return axios($, {
+ url: "https://api.frankfurter.app/currencies",
+ })
+ .catch(error => {
+ console.error("Error fetching currencies:", error);
+ throw new Error(`Failed to fetch currency list: ${error.message}`);
+ });
}Similar error handling should be added to convertCurrency as well.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| getCurrencies($ = this) { | |
| return axios($, { | |
| url: "https://api.frankfurter.app/currencies", | |
| }); | |
| }, | |
| getCurrencies($ = this) { | |
| return axios($, { | |
| url: "https://api.frankfurter.app/currencies", | |
| }) | |
| .catch(error => { | |
| console.error("Error fetching currencies:", error); | |
| throw new Error(`Failed to fetch currency list: ${error.message}`); | |
| }); | |
| }, |
| value: { | ||
| type: "string", | ||
| label: "Value", | ||
| description: "The value to convert", | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Validate currency value is a number.
The value property is defined as a string type, but should be a valid number for currency conversion. Consider adding validation or changing the type:
value: {
- type: "string",
+ type: "integer",
label: "Value",
description: "The value to convert",
+ validate: (value) => {
+ const num = Number(value);
+ if (isNaN(num)) {
+ return "Value must be a valid number";
+ }
+ return true;
+ },
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| value: { | |
| type: "string", | |
| label: "Value", | |
| description: "The value to convert", | |
| }, | |
| value: { | |
| type: "integer", | |
| label: "Value", | |
| description: "The value to convert", | |
| validate: (value) => { | |
| const num = Number(value); | |
| if (isNaN(num)) { | |
| return "Value must be a valid number"; | |
| } | |
| return true; | |
| }, | |
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
♻️ Duplicate comments (1)
components/pipedream_utils/actions/format-currency/format-currency.mjs (1)
58-69:⚠️ Potential issueFix the default clause position in switch statement
The default clause should be the last switch clause according to best practices. This was also flagged by the static analysis tool.
switch (currencyFormat.match(/¤+$/g)?.[0].length) { - default: - break; // ¤¤ - ISO currency symbol: USD, BRL, etc. case 2: result += ` ${isoCode}`; break; // ¤¤¤ - Currency display name: United States dollar, Brazilian real, etc. case 3: result += ` ${currencyName}`; break; + default: + break; }🧰 Tools
🪛 Biome (1.9.4)
[error] 59-61: The default clause should be the last switch clause.
The following case clause is here:
Regardless its position, the default clause is always executed when there is no match. To avoid confusion, the default clause should be the last switch clause.
(lint/suspicious/useDefaultSwitchClauseLast)
🧹 Nitpick comments (9)
components/pipedream_utils/actions/replace-text/replace-text.mjs (5)
33-38: Consider using a more robust method for RegExp detection.The current approach of checking if
findTextstarts with a slash is a simplistic way to determine if the input is a regular expression. This may not handle all valid regex patterns correctly. Additionally, always adding the 'g' flag may not be appropriate for all use cases.- const isRegExp = findText.startsWith("/"); - const expression = isRegExp - ? buildRegExp(findText, [ - "g", - ]) - : findText; + let isRegExp = false; + let expression = findText; + + // Check if input is intended to be a regular expression + if (findText.startsWith("/")) { + try { + expression = buildRegExp(findText, ["g"]); + isRegExp = true; + } catch (error) { + // Fallback to literal string if regex construction fails + $.export("$summary", `Warning: Invalid regular expression format. Treating as literal text.`); + } + }
40-50: Optimize the replacement logic for better performance.The current implementation uses a loop with
matchandreplacefor non-regex replacements, which can be inefficient for large inputs. The comment indicatesreplaceAllisn't supported in Node 14.x, but if newer Node versions are supported, this could be simplified.- // replaceAll would be optimal, but it is not supported in Node 14.x - let result = input.replace(expression, replaceValue); - if (!isRegExp) { - let counter = 0; - while (result.match(expression)) { - result = result.replace(expression, replaceValue); - if (++counter > MAX_REPLACES) { - throw new Error(`Over ${MAX_REPLACES} replace operations reached - please check your inputs and, if this is expected, search with a regular expression instead of text`); - } - } - } + // Use a more efficient approach for text replacement + let result; + if (isRegExp) { + result = input.replace(expression, replaceValue); + } else { + // For newer Node versions, use replaceAll if available + if (typeof input.replaceAll === 'function') { + result = input.replaceAll(findText, replaceValue); + } else { + // Fallback for Node 14.x + result = input.replace(new RegExp(findText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), replaceValue); + } + }
51-58: Improve the match counting logic.The current match counting logic mixes boolean and text values in a potentially confusing way. It would be clearer to count actual replacements uniformly for both regex and non-regex scenarios.
- const matchText = isRegExp - ? [ - ...input.matchAll(expression), - ].length - : input.includes(findText) && "text"; - $.export("$summary", matchText - ? `Successfully replaced ${matchText} matches` - : "No matches found. Input was not modified"); + let matchCount = 0; + if (isRegExp) { + matchCount = [...input.matchAll(expression)].length; + } else { + // Count occurrences of findText in input + let pos = input.indexOf(findText); + while (pos !== -1) { + matchCount++; + pos = input.indexOf(findText, pos + findText.length); + } + } + + $.export("$summary", matchCount > 0 + ? `Successfully replaced ${matchCount} matches` + : "No matches found. Input was not modified");
47-47: Enhance the error message to provide better guidance.When suggesting users switch to regular expressions, the error message could be more helpful by providing an example of how to format their input as a regex.
- throw new Error(`Over ${MAX_REPLACES} replace operations reached - please check your inputs and, if this is expected, search with a regular expression instead of text`); + throw new Error(`Over ${MAX_REPLACES} replace operations reached - please check your inputs. If this is expected, use a regular expression instead (e.g., "/your-text-here/g").`);
19-19: Enhance the regex pattern description.The description provides a link to learn about regular expressions but could be more helpful by including basic examples of common regex patterns users might need.
- description: "The text you would like to search for and replace. This can also be a string representing a [Regular Expression](https://www.w3schools.com/js/js_regexp.asp)", + description: "The text you would like to search for and replace. This can also be a string representing a [Regular Expression](https://www.w3schools.com/js/js_regexp.asp). Examples: '/pattern/i' (case-insensitive), '/\\d+/g' (all numbers).",components/pipedream_utils/actions/format-number/format-number.mjs (1)
38-38: Consider trimming the input to handle whitespaceThe input is converted to a string but not trimmed, which might cause issues if it contains leading or trailing whitespace.
- const input = this.input.toString(); + const input = this.input.toString().trim();components/pipedream_utils/actions/format-currency/format-currency.mjs (3)
52-56: Simplify decimal part handling logicThe current implementation for handling the decimal part is complex and can be simplified for better readability.
- const numberString = formatNumber(integer, (decimal?.length > 1 - ? decimal - : (decimal ?? "0") + "0"), currencyFormat.includes(",") - ? "," - : ""); + let decimalPart = decimal ?? "0"; + // Ensure at least 2 decimal places for currency + if (decimalPart.length < 2) { + decimalPart += "0"; + } + const groupingSeparator = currencyFormat.includes(",") ? "," : ""; + const numberString = formatNumber(integer, decimalPart, groupingSeparator, ".");
36-36: Consider trimming the input to handle whitespaceThe input is converted to a string but not trimmed, which might cause issues if it contains leading or trailing whitespace.
- const input = this.input.toString(); + const input = this.input.toString().trim();
42-42: Be explicit about the initial result valueThe current code has a complex conditional expression to set the initial result value. This could be made more readable with an explicit if statement.
- let result = (currencyFormat.startsWith("¤") && currencySymbol) || ""; + let result = ""; + if (currencyFormat.startsWith("¤") && currencySymbol) { + result = currencySymbol; + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (22)
components/pipedream_utils/actions/add-subtract-time/add-subtract-time.mjs(1 hunks)components/pipedream_utils/actions/compare-dates/compare-dates.mjs(1 hunks)components/pipedream_utils/actions/convert-html-to-markdown/convert-html-to-markdown.mjs(1 hunks)components/pipedream_utils/actions/convert-html-to-text/convert-html-to-text.mjs(1 hunks)components/pipedream_utils/actions/convert-json-to-string/convert-json-to-string.mjs(1 hunks)components/pipedream_utils/actions/convert-markdown-to-html/convert-markdown-to-html.mjs(1 hunks)components/pipedream_utils/actions/date-time-format/date-time-format.mjs(1 hunks)components/pipedream_utils/actions/extract-by-regular-expression/extract-by-regular-expression.mjs(1 hunks)components/pipedream_utils/actions/extract-email-address/extract-email-address.mjs(1 hunks)components/pipedream_utils/actions/extract-number/extract-number.mjs(1 hunks)components/pipedream_utils/actions/extract-phone-number/extract-phone-number.mjs(1 hunks)components/pipedream_utils/actions/extract-url/extract-url.mjs(1 hunks)components/pipedream_utils/actions/format-currency/format-currency.mjs(1 hunks)components/pipedream_utils/actions/format-number/format-number.mjs(1 hunks)components/pipedream_utils/actions/parse-json/parse-json.mjs(1 hunks)components/pipedream_utils/actions/replace-text/replace-text.mjs(1 hunks)components/pipedream_utils/actions/set-default-value/set-default-value.mjs(1 hunks)components/pipedream_utils/actions/split-text/split-text.mjs(1 hunks)components/pipedream_utils/actions/transform-case/transform-case.mjs(1 hunks)components/pipedream_utils/actions/trim-whitespace/trim-whitespace.mjs(1 hunks)components/pipedream_utils/actions/url-decode/url-decode.mjs(1 hunks)components/pipedream_utils/actions/url-encode/url-encode.mjs(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (19)
- components/pipedream_utils/actions/date-time-format/date-time-format.mjs
- components/pipedream_utils/actions/convert-html-to-text/convert-html-to-text.mjs
- components/pipedream_utils/actions/convert-markdown-to-html/convert-markdown-to-html.mjs
- components/pipedream_utils/actions/trim-whitespace/trim-whitespace.mjs
- components/pipedream_utils/actions/url-encode/url-encode.mjs
- components/pipedream_utils/actions/extract-url/extract-url.mjs
- components/pipedream_utils/actions/extract-number/extract-number.mjs
- components/pipedream_utils/actions/extract-email-address/extract-email-address.mjs
- components/pipedream_utils/actions/extract-phone-number/extract-phone-number.mjs
- components/pipedream_utils/actions/url-decode/url-decode.mjs
- components/pipedream_utils/actions/set-default-value/set-default-value.mjs
- components/pipedream_utils/actions/split-text/split-text.mjs
- components/pipedream_utils/actions/convert-html-to-markdown/convert-html-to-markdown.mjs
- components/pipedream_utils/actions/add-subtract-time/add-subtract-time.mjs
- components/pipedream_utils/actions/transform-case/transform-case.mjs
- components/pipedream_utils/actions/compare-dates/compare-dates.mjs
- components/pipedream_utils/actions/convert-json-to-string/convert-json-to-string.mjs
- components/pipedream_utils/actions/parse-json/parse-json.mjs
- components/pipedream_utils/actions/extract-by-regular-expression/extract-by-regular-expression.mjs
🧰 Additional context used
🪛 Biome (1.9.4)
components/pipedream_utils/actions/format-currency/format-currency.mjs
[error] 59-61: The default clause should be the last switch clause.
The following case clause is here:
Regardless its position, the default clause is always executed when there is no match. To avoid confusion, the default clause should be the last switch clause.
(lint/suspicious/useDefaultSwitchClauseLast)
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: Verify TypeScript components
- GitHub Check: Publish TypeScript components
🔇 Additional comments (2)
components/pipedream_utils/actions/replace-text/replace-text.mjs (2)
1-3: The MAX_REPLACES constant is well-defined.Setting a maximum number of replacements is a good safeguard against potential infinite loops or performance issues with large inputs. This is especially important in a cloud environment where resources may be constrained.
10-28: Props are well-structured with clear descriptions.The component properties are well-defined with appropriate labels, descriptions, and types. Making the replacement text optional with a clear indication that it defaults to deleting the found text provides good flexibility for users.
| name: "Formatting - [Text] Replace Text", | ||
| description: "Replace all instances of any character, word or phrase in the text with another character, word or phrase.", | ||
| key: "pipedream_utils-replace-text", | ||
| version: "0.0.5", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Version number indicates early development.
The component version is set to "0.0.5", which suggests it might be in early development. Consider clarifying the versioning strategy or incrementing to a more appropriate version if this component is ready for production use.
🌐 Web query:
What are best practices for semantic versioning in npm packages?
💡 Result:
Semantic versioning (SemVer) in npm packages ensures predictable dependency management by communicating the impact of changes through version numbers (MAJOR.MINOR.PATCH). Here are key best practices:
Core Principles
- MAJOR for breaking API changes (e.g.,
1.0.0→2.0.0) [2][4]. - MINOR for backward-compatible features (e.g.,
1.0.0→1.1.0) [2][5]. - PATCH for backward-compatible bug fixes (e.g.,
1.0.0→1.0.1) [2][4]. - Start production-ready packages at
1.0.0[5][4].
Version Ranges & Symbols
Use version specifiers to control updates:
| Symbol | Rule | Example (Range ~1.2.3) |
|---|---|---|
^ |
Allow minor/patch updates | ^1.2.3 → 1.3.0, 1.2.4 [2][8]. |
~ |
Allow only patch updates | ~1.2.3 → 1.2.4 (not 1.3.0) [2][8]. |
= |
Pin to exact version | =1.2.3 → No updates [2]. |
Example Dependency Entry:
"dependencies": {
"lodash": "^4.17.21", // Allows minor/patch updates
"react": "~18.2.0" // Allows only patch updates
}Pre-Release Handling
- Use pre-release tags (e.g.,
1.0.0-alpha.1) instead of custom distribution tags (beta,latest) to avoid accidental breaking changes [1][3]. - Automate tagging in CI/CD:
npm version prerelease --preid=beta # Bumps to 1.0.0-beta.0 npm publish --tag beta # Publishes under beta tag
Automation & Workflow
- Script Integration: Automate version bumps and publishing:
# Bump patch version, publish, and push Git tags npm version patch && npm publish && git push --follow-tags
- CI/CD: Use tools to auto-increment versions and run tests before release [1][3].
Avoid Common Pitfalls
- Never mutate published versions: Use immutable tags like
-rc.1instead of reassigningbeta[1]. - Lock dependencies: Use
package-lock.jsonto ensure reproducible builds [6][8]. - Update frequently: Regularly run
npm updateto stay current with non-breaking changes [6].
Organizational Standards
- Define team-wide scopes (e.g.,
@myorg/utils) to group related packages [1]. - Enforce consistent metadata in
package.json(author, license, repository) [1][5].
By following these practices, teams reduce dependency conflicts, streamline releases, and maintain trust in package updates.
Citations:
- 1: https://blog.inedo.com/npm/best-practices-for-your-organizations-npm-packages/
- 2: https://gist.github.com/jonlabelle/706b28d50ba75bf81d40782aa3c84b3e
- 3: https://blog.bajonczak.com/versioning-in-npm/
- 4: https://semver.org
- 5: https://docs.npmjs.com/about-semantic-versioning/
- 6: https://www.datree.io/resources/npm-versioning-guide
- 7: https://docs.npmjs.com/files/package.json/
- 8: https://dev.to/typescripttv/understanding-npm-versioning-3hn4
Action Required: Update Version Number for Production Readiness
- File:
components/pipedream_utils/actions/replace-text/replace-text.mjs(Line 8) - Issue: The current version
"0.0.5"suggests that the component is still in early development. - Recommendation: According to semantic versioning best practices, production-ready packages should start at
1.0.0. If this component is intended for production, please update the version number accordingly. Alternatively, if it remains under active early development, consider adding documentation that clearly outlines your versioning strategy to avoid confusion.
| async run({ $ }) { | ||
| const { | ||
| inputDecimalMark, toFormat, | ||
| } = this; | ||
| const input = this.input.toString(); | ||
| const decimalMark = inputDecimalMark ?? "."; | ||
| const splitInput = input.split(decimalMark); | ||
| if (splitInput.length > 2) { | ||
| throw new ConfigurationError(`Input has more than one decimal mark (\`${decimalMark}\`). Check if the \`Input Decimal Mark\` prop is set correctly.`); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add proper number validation to prevent invalid inputs
The current code only validates if the input has more than one decimal mark, but it doesn't check if the input is actually a valid number. You should add validation similar to what's implemented in the format-currency.mjs file.
const input = this.input.toString();
const decimalMark = inputDecimalMark ?? ".";
const splitInput = input.split(decimalMark);
if (splitInput.length > 2) {
throw new ConfigurationError(`Input has more than one decimal mark (\`${decimalMark}\`). Check if the \`Input Decimal Mark\` prop is set correctly.`);
}
+ const [integer, decimal] = splitInput;
+ if (isNaN(Number(integer))) {
+ throw new ConfigurationError("**Invalid number** - please check your input.");
+ }
- const [
- integer,
- decimal,
- ] = splitInput;Committable suggestion skipped: line range outside the PR's diff.
| const [ | ||
| groupChar, | ||
| decimalChar, | ||
| ] = toFormat.split(""); | ||
| const result = formatNumber(integer, decimal, groupChar, decimalChar); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Add validation for the toFormat property
The code assumes that toFormat will always have at least two characters, but there's no validation to handle cases where toFormat might be too short.
const [
groupChar,
decimalChar,
+ ] = toFormat.split("").length >= 2 ? toFormat.split("") : ["", ""];
- ] = toFormat.split("");
const result = formatNumber(integer, decimal, groupChar, decimalChar);Run this script to verify how toFormat values are defined and used across the codebase:
🏁 Script executed:
#!/bin/bash
# Check the possible values of toFormat and their structure
grep -r "FINAL_FORMAT_OPTIONS" --include="*.mjs" .
cat $(find . -name "numberFormattingOptions.mjs")Length of output: 995
Action Required: Add Validation for toFormat
The code currently assumes that toFormat always contains at least two characters. Even though our defined options in FINAL_FORMAT_OPTIONS (in components/pipedream_utils/common/numbers/numberFormattingOptions.mjs) always provide two-character strings, we should not rely solely on that assumption. To safeguard against any unexpected or dynamic inputs, please add proper validation as suggested below.
- Location:
components/pipedream_utils/actions/format-number/format-number.mjs(Lines 48-52) - Proposed Change: Validate that splitting
toFormatyields at least two characters before destructuring; otherwise, fall back to a safe default. - Diff:
const splitFormat = toFormat.split(""); - const [groupChar, decimalChar] = splitFormat;
-
const [groupChar, decimalChar] = splitFormat.length >= 2 ? splitFormat : ["", ""];
const result = formatNumber(integer, decimal, groupChar, decimalChar);
<!-- suggestion_start -->
<details>
<summary>📝 Committable suggestion</summary>
> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
`````suggestion
const splitFormat = toFormat.split("");
const [groupChar, decimalChar] = splitFormat.length >= 2 ? splitFormat : ["", ""];
const result = formatNumber(integer, decimal, groupChar, decimalChar);
| isoCode, | ||
| currencySymbol, | ||
| currencyName, | ||
| ] = currency.split(" - "); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add validation for currency format
The code assumes the currency string will always split into three parts with " - " as the separator, but there's no validation to handle cases where the format might be different.
const [
isoCode,
currencySymbol,
currencyName,
] = currency.split(" - ");
+
+ if (!isoCode || !currencySymbol || !currencyName) {
+ throw new ConfigurationError("Invalid currency format. Expected format: 'ISO - Symbol - Name'");
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ] = currency.split(" - "); | |
| const [ | |
| isoCode, | |
| currencySymbol, | |
| currencyName, | |
| ] = currency.split(" - "); | |
| if (!isoCode || !currencySymbol || !currencyName) { | |
| throw new ConfigurationError("Invalid currency format. Expected format: 'ISO - Symbol - Name'"); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (6)
components/pipedream_utils/common/date-time/commonDateTime.mjs (6)
24-39: Improve error handling and input validation in getDateFromInput methodThe current error handling captures all exceptions and treats them as date parsing errors. Consider validating inputs before processing and providing more specific error handling.
getDateFromInput(date = this.inputDate, format = this.inputFormat) { let dateObj; try { + if (!date) { + throw new ConfigurationError("Input date is empty or undefined"); + } const inputFn = DATE_FORMAT_PARSE_MAP.get(format)?.inputFn ?? DEFAULT_INPUT_FUNCTION; dateObj = inputFn(date); if (isNaN(dateObj.getFullYear())) throw new Error("Invalid date"); } catch (err) { + if (err instanceof ConfigurationError) { + throw err; + } throw new ConfigurationError(`**Error** parsing input \`${date}\` ${format ? `expecting specified format \`${format}\`` : "- try selecting a format in the **Input Format** prop."}`); } return dateObj; },
27-28: Simplify optional chaining and nullish coalescing expressionsThe current syntax spans two lines and may reduce readability. Consider refactoring to a more concise format.
- const inputFn = DATE_FORMAT_PARSE_MAP.get(format)?.inputFn ?? - DEFAULT_INPUT_FUNCTION; + const inputFn = DATE_FORMAT_PARSE_MAP.get(format)?.inputFn ?? DEFAULT_INPUT_FUNCTION;
4-5: Consider adding type or interface documentation for imported utilitiesAdding a brief comment about the structure and purpose of
DATE_FORMAT_PARSE_MAPandDEFAULT_INPUT_FUNCTIONwould improve code readability and maintenance.import { - DATE_FORMAT_PARSE_MAP, DEFAULT_INPUT_FUNCTION, + // Map of format strings to their corresponding parsing functions + DATE_FORMAT_PARSE_MAP, + // Default function for parsing dates when no specific format is provided + DEFAULT_INPUT_FUNCTION, } from "./dateFormats.mjs";
30-31: Provide more specific error message for invalid datesThe current error message "Invalid date" is generic. A more specific message would help users understand what went wrong.
- if (isNaN(dateObj.getFullYear())) - throw new Error("Invalid date"); + if (isNaN(dateObj.getFullYear())) { + throw new Error(`Invalid date: '${date}' could not be parsed as a valid date`); + }
34-36: Improve error message formatting for better readabilityThe current template string spans multiple lines with concatenation. Consider using a more readable format.
- throw new ConfigurationError(`**Error** parsing input \`${date}\` ${format - ? `expecting specified format \`${format}\`` - : "- try selecting a format in the **Input Format** prop."}`); + throw new ConfigurationError( + `**Error** parsing input \`${date}\` ${ + format + ? `expecting specified format \`${format}\`` + : "- try selecting a format in the **Input Format** prop." + }` + );
6-41: Add JSDoc comments to improve documentationAdding JSDoc comments to the exported object and its methods would improve code documentation and provide better editor support.
+/** + * Common date-time utilities for Pipedream + * @module commonDateTime + */ export default { props: { pipedream_utils, inputDate: { description: "A valid date string, in the format selected in **Input Format**. If the format is not set, Pipedream will attempt to infer it using the parser from [Sugar Date library](https://sugarjs.com/dates/#/Parsing).", propDefinition: [ pipedream_utils, "inputDate", ], }, inputFormat: { propDefinition: [ pipedream_utils, "inputFormat", ], }, }, methods: { + /** + * Parses a date string according to the specified format + * @param {string} [date=this.inputDate] - The date string to parse + * @param {string} [format=this.inputFormat] - The format to use for parsing + * @returns {Date} A JavaScript Date object + * @throws {ConfigurationError} If the date cannot be parsed + */ getDateFromInput(date = this.inputDate, format = this.inputFormat) { // Method implementation... }, }, };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
components/pipedream_utils/common/date-time/commonDateTime.mjs(1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Pull Request Checks
components/pipedream_utils/common/date-time/commonDateTime.mjs
[error] 1-1: Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/home/runner/work/pipedream/pipedream/components/pipedream_utils/app/pipedream_utils.app.mjs' imported from /home/runner/work/pipedream/pipedream/components/pipedream_utils/common/date-time/commonDateTime.mjs
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: pnpm publish
- GitHub Check: Verify TypeScript components
- GitHub Check: Publish TypeScript components
| @@ -0,0 +1,41 @@ | |||
| import pipedream_utils from "../../app/pipedream_utils.app.mjs"; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the module import path for pipedream_utils
The import path "../../app/pipedream_utils.app.mjs" is causing a build failure as shown in the pipeline logs. The module cannot be found at this location.
-import pipedream_utils from "../../app/pipedream_utils.app.mjs";
+import pipedream_utils from "../../../pipedream_utils/app/pipedream_utils.app.mjs";The path should be adjusted to match the actual location of the module in the repository structure.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import pipedream_utils from "../../app/pipedream_utils.app.mjs"; | |
| import pipedream_utils from "../../../pipedream_utils/app/pipedream_utils.app.mjs"; |
🧰 Tools
🪛 GitHub Actions: Pull Request Checks
[error] 1-1: Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/home/runner/work/pipedream/pipedream/components/pipedream_utils/app/pipedream_utils.app.mjs' imported from /home/runner/work/pipedream/pipedream/components/pipedream_utils/common/date-time/commonDateTime.mjs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
components/pipedream_utils/package.json (1)
15-33: New Dependency AdditionsA new
"dependencies"section has been introduced with a comprehensive list of packages (including@pipedream/platform,csv-parse,html-to-text,linkedom, and others). Verify that:
- Each dependency is necessary for the expanded functionality of the Pipedream Utils components.
- The version constraints using caret (
^) align with your stability and upgrade policies.- Any related documentation (like updates in README.md) reflects these new dependencies.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (1)
components/pipedream_utils/package.json(2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: Verify TypeScript components
- GitHub Check: Publish TypeScript components
🔇 Additional comments (2)
components/pipedream_utils/package.json (2)
3-3: Version Bump UpdateThe version has been updated to "0.0.2". Ensure that this version increment aligns with your release notes and versioning strategy.
12-14: PublishConfig Formatting VerificationThe
"publishConfig"block now explicitly sets"access": "public". Confirm that this configuration meets your publishing requirements and that no unintended formatting changes have been applied.
Merging helper functions and formatting components
Summary by CodeRabbit