From fa2e5cc0de116ab61b66a3b5e1f8d09a4faf418d Mon Sep 17 00:00:00 2001 From: saurabhdaware Date: Thu, 1 Aug 2024 17:15:56 +0530 Subject: [PATCH 1/8] feat: move aicodemod to blade repo --- packages/blade/codemods/aicodemod/config.mjs | 7 + .../codemods/aicodemod/getChatCompletion.mjs | 57 ++ packages/blade/codemods/aicodemod/index.mjs | 84 +++ .../codemods/aicodemod/knowledge/Table.md | 596 ++++++++++++++++++ packages/blade/codemods/aicodemod/server.mjs | 27 + packages/blade/package.json | 4 +- yarn.lock | 318 +++++++++- 7 files changed, 1059 insertions(+), 34 deletions(-) create mode 100644 packages/blade/codemods/aicodemod/config.mjs create mode 100644 packages/blade/codemods/aicodemod/getChatCompletion.mjs create mode 100755 packages/blade/codemods/aicodemod/index.mjs create mode 100644 packages/blade/codemods/aicodemod/knowledge/Table.md create mode 100644 packages/blade/codemods/aicodemod/server.mjs diff --git a/packages/blade/codemods/aicodemod/config.mjs b/packages/blade/codemods/aicodemod/config.mjs new file mode 100644 index 00000000000..a53942b72f8 --- /dev/null +++ b/packages/blade/codemods/aicodemod/config.mjs @@ -0,0 +1,7 @@ +export const aiConfig = { + temperature: 0.7, + top_p: 0.95, + max_tokens: 1000, + // Doesn't make actual API call when set to true. Instead prints the input itself + testMode: true, +}; diff --git a/packages/blade/codemods/aicodemod/getChatCompletion.mjs b/packages/blade/codemods/aicodemod/getChatCompletion.mjs new file mode 100644 index 00000000000..4e0defe810c --- /dev/null +++ b/packages/blade/codemods/aicodemod/getChatCompletion.mjs @@ -0,0 +1,57 @@ +import { aiConfig } from './config.mjs'; + +const getChatCompletion = async (messages) => { + const { testMode, ...payloadConfig } = aiConfig; + + if (testMode) { + console.log('messages:'); + console.dir(messages, { depth: Infinity }); + return { + answer: + 'The server is running in testMode. Its not supposed to. Blame @Saurabh for this on slack #design-system channel.', + usage: { total_tokens: 0 }, + inputLength: JSON.stringify(messages).length, + }; + } + + const headers = { + 'Content-Type': 'application/json', + 'api-key': process.env.OPENAI_API_KEY, + }; + + // Payload for the request + const payload = { + messages, + ...payloadConfig, + }; + + const GPT4V_ENDPOINT = + 'https://design-system-blade.openai.azure.com/openai/deployments/Designsystem/chat/completions?api-version=2024-02-15-preview'; + + // Send request + let data; + try { + data = await fetch(GPT4V_ENDPOINT, { + method: 'POST', + headers: headers, + body: JSON.stringify(payload), + }).then((response) => { + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + return response.json(); + }); + } catch (error) { + console.error(`Failed to make the request. Error: ${error}`); + } + + console.log('>> USAGE', data.model, data.usage); + + return { + answer: data.choices[0].message, + usage: data.usage, + inputLength: JSON.stringify(messages).length, + }; +}; + +export { getChatCompletion }; diff --git a/packages/blade/codemods/aicodemod/index.mjs b/packages/blade/codemods/aicodemod/index.mjs new file mode 100755 index 00000000000..b26321ee9d2 --- /dev/null +++ b/packages/blade/codemods/aicodemod/index.mjs @@ -0,0 +1,84 @@ +#!/usr/bin/env node +import fs from 'fs'; +import path from 'path'; + +// This is our own server from where we call openai +const SERVER_BASE_URL = ''; +const SERVER_ENDPOINT = '/chat/completions'; + +const getChatCompletionFromServer = async (messages) => { + const url = SERVER_BASE_URL + SERVER_ENDPOINT; + + // Define the fetch options + const options = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ messages }), + }; + + // Make the fetch call + const response = await fetch(url, options); + return response.json(); +}; + +const __dirname = new URL('.', import.meta.url).pathname; +const cwd = process.cwd(); + +const codeKnowledge = fs.readFileSync(path.join(cwd, 'codeKnowledge.md'), 'utf-8'); +const bladeKnowledge = fs.readFileSync(path.resolve(__dirname, './knowledge/Table.md'), 'utf-8'); + +const usageFileArg = process.argv[2]; +const usageFilePath = path.join(cwd, usageFileArg); +const usage = fs.readFileSync(usageFilePath, 'utf-8'); + +const ultimatePrompt = ` +I want you to migrate our old codebase code from custom implementation, to use our design-system component called Blade. + +You fill find code snippets of these things below- + +1. Custom Component Definitions: Custom component implementations to help figure out what they do +4. Blade's Table Documentation: Documentation of our design-system table +3. Current Usage: Usage of the custom implimented component + +Return Format: +- Refer to Custom component definitions, and Blade's Table documentation and return the migrated code snippet of custom usage using Blade +- Do not add any additional text +- Don't change unrelated code and imports +- Make sure Blade's Header and Footer are also used wherever needed. +- Ignore the code related to responsiveness as Blade components are responsive by default + +## Custom Component Definitions +${codeKnowledge} + +## Blade's Table Documentation +${bladeKnowledge} + +## Current Usage +${usage} +`; + +const messages = [ + { + role: 'system', + content: [ + { + type: 'text', + text: + 'You are an AI assistant that helps in migrating codebase from old custom implementation to our internal design-system components', + }, + ], + }, + { + role: 'user', + content: ultimatePrompt, + }, +]; + +const data = await getChatCompletionFromServer(messages); +console.log('USAGE STATS', { + inputLength: data.inputLength, + openAIUsage: data.usage, +}); +fs.writeFileSync(usageFilePath, data.answer.content); diff --git a/packages/blade/codemods/aicodemod/knowledge/Table.md b/packages/blade/codemods/aicodemod/knowledge/Table.md new file mode 100644 index 00000000000..30d77d70df7 --- /dev/null +++ b/packages/blade/codemods/aicodemod/knowledge/Table.md @@ -0,0 +1,596 @@ +# Table + +A table component helps in displaying data in a grid format, through rows and columns of cells. Table facilitates data organisation and allow users to: scan, sort, compare, and take action on large amounts of data. + +## Props + +```ts +type TableNode = Item & { + id: Identifier; +}; + +type TableData = { + nodes: TableNode[]; +}; + +type TableBackgroundColors = `surface.background.gray.${DotNotationToken< + Theme['colors']['surface']['background']['gray'] +>}`; + +type TableHeaderProps = { + /** + * The children of TableHeader should be TableHeaderRow + * @example + * + * + * Header Cell 1 + * + * + **/ + children: React.ReactNode; +}; + +type TableHeaderRowProps = { + /** + * The children of TableHeaderRow should be TableHeaderCell + * @example + * + * + * Header Cell 1 + * + * + **/ + children: React.ReactNode; + /** + * The rowDensity prop determines the density of the table. + * The rowDensity prop can be 'compact', 'normal', or'comfortable'. + * The default value is `normal`. + **/ + rowDensity?: TableProps['rowDensity']; +}; + +type TableHeaderCellProps = { + /** + * The children of TableHeaderCell can be a string or a ReactNode. + **/ + children: string | React.ReactNode; + /** + * The unique key of the column. + * This is used to identify the column for sorting in sortFunctions prop of Table. + * Sorting is enabled only for columns whose key is present in sortableColumns prop of Table. + **/ + headerKey?: string; +}; + +type TableProps = { + /** + * The children of the Table component should be a function that returns TableHeader, TableBody and TableFooter components. + * The function will be called with the tableData prop. + */ + children: (tableData: TableNode[]) => React.ReactElement; + /** + * The data prop is an object with a nodes property that is an array of objects. + * Each object in the array is a row in the table. + * The object should have an id property that is a unique identifier for the row. + */ + data: TableData; + /** + * Selection mode determines how the table rows can be selected. + * @default 'row' + **/ + multiSelectTrigger?: 'checkbox' | 'row'; + /** + * The selectionType prop determines the type of selection that is allowed on the table. + * The selectionType prop can be 'none', 'single' or 'multiple'. + * @default 'none' + **/ + selectionType?: 'none' | 'single' | 'multiple'; + /** + * The onSelectionChange prop is a function that is called when the selection changes. + * The function is called with an object that has a values property that is an array of the selected rows. + **/ + onSelectionChange?: ({ + values, + selectedIds, + }: { + /** + * Note: on server side paginated data, this prop will only contain the selected rows on the current page. + * + * Thus, it's recommended to use `selectedIds` for more consistent state management across server/client paginated data. + * + * *Deprecated:* Use `selectedIds` instead. + * + * @deprecated + */ + values: TableNode[]; + /** + * An array of selected row ids. + */ + selectedIds: Identifier[]; + }) => void; + /** + * The isHeaderSticky prop determines whether the table header is sticky or not. + * The default value is `false`. + **/ + isHeaderSticky?: boolean; + /** + * The isFooterSticky prop determines whether the table footer is sticky or not. + * The default value is `false`. + **/ + isFooterSticky?: boolean; + /** + * The isFirstColumnSticky prop determines whether the first column is sticky or not. + * The default value is `false`. + **/ + isFirstColumnSticky?: boolean; + /** + * The rowDensity prop determines the density of the table. + * The rowDensity prop can be 'compact', 'normal', or'comfortable'. + * The default value is `normal`. + **/ + rowDensity?: 'compact' | 'normal' | 'comfortable'; + /** + * The onSortChange prop is a function that is called when the sort changes. + * The function is called with an object that has a sortKey property that is the key of the column that is sorted and a isSortReversed property that is a boolean that determines whether the sort is reversed or not. + **/ + onSortChange?: ({ + sortKey, + isSortReversed, + }: { + sortKey: TableHeaderCellProps['headerKey']; + isSortReversed: boolean; + }) => void; + /** + * The sortFunctions prop is an object that has a key for each column that is sortable. + * The value of each key is a function that is called when the column is sorted. + * The function is called with an array of the rows in the table. + * The function should return an array of the rows in the table. + **/ + sortFunctions?: Record[]) => TableNode[]>; + /** + * The toolbar prop is a React element that is rendered above the table. + * The toolbar prop should be a `TableToolbar` component. + **/ + toolbar?: React.ReactElement; + /** + * The pagination prop is a React element that is rendered below the table. + * The pagination prop should be a `TablePagination` component. + **/ + pagination?: React.ReactElement; + /** + * The height prop is a responsive styled prop that determines the height of the table. + **/ + height?: BoxProps['height']; + /** + * The showStripedRows prop determines whether the table should have striped rows or not. + * The default value is `false`. + **/ + showStripedRows?: boolean; + /** + * The gridTemplateColumns prop determines the grid-template-columns CSS property of the table. + * The default value is `repeat(${columnCount},minmax(100px, 1fr))`. + **/ + gridTemplateColumns?: string; + /** + * The isLoading prop determines whether the table is loading or not. + * The default value is `false`. + **/ + isLoading?: boolean; + /** + * The isRefreshing prop determines whether the table is refreshing or not. + * The default value is `false`. + **/ + isRefreshing?: boolean; + /** + * The showBorderedCells prop determines whether the table should have bordered cells or not. + **/ + showBorderedCells?: boolean; +} & StyledPropsBlade; + +type Identifier = string | number; + +type TableBodyProps = { + /** + * The children of the TableBody component should be TableRow components. + * @example + * + * + * ... + * + * + **/ + children: React.ReactNode; +}; + +type TableRowProps = { + /** + * The children of the TableRow component should be TableCell components. + * @example + * + * ... + * + **/ + children: React.ReactNode; + /** + * The item prop is used to pass the individual table item to the TableRow component. + * @example + * tableData.map((tableItem) => ( + * + * ... + * + * )); + **/ + item: TableNode; + /** + * The isDisabled prop is used to disable the TableRow component. + * @example + * + * ... + * + **/ + isDisabled?: boolean; + /** + * Callback triggered when the row is hovered. It is called with the current row item prop. + */ + onHover?: ({ item }: { item: TableNode }) => void; + /** + * Callback triggered when the row is clicked. It is called with the current row item prop. + */ + onClick?: ({ item }: { item: TableNode }) => void; +}; + +type TableCellProps = { + /** + * The children of the TableCell component should be a string or a ReactNode. + * @example + * {'Hello'} + * + * ... + * + * + * + * + **/ + children: React.ReactNode; +}; + +type TableEditableCellProps = Pick< + BaseInputProps, + | 'validationState' + | 'placeholder' + | 'defaultValue' + | 'name' + | 'onChange' + | 'onFocus' + | 'onBlur' + | 'value' + | 'isDisabled' + | 'isRequired' + | 'prefix' + | 'suffix' + | 'maxCharacters' + | 'autoFocus' + | 'keyboardReturnKeyType' + | 'autoCompleteSuggestionType' + | 'onSubmit' + | 'autoCapitalize' + | 'testID' + | 'onClick' + | 'leadingIcon' + | 'trailingButton' + | 'errorText' + | 'successText' +> & { + accessibilityLabel: NonNullable; +}; + +type TableFooterProps = { + /** + * The children of TableFooter should be TableFooterRow + * @example + * + * + * Footer Cell 1 + * + * + **/ + children: React.ReactNode; +}; + +type TableFooterRowProps = { + /** + * The children of TableFooterRow should be TableFooterCell + * @example + * + * + * Footer Cell 1 + * + * + **/ + children: React.ReactNode; +}; + +type TableFooterCellProps = { + /** + * The children of TableHeaderCell can be a string or a ReactNode. + **/ + children: string | React.ReactNode; +}; + +type TablePaginationCommonProps = { + /** + * The default page size. + * Page size controls how rows are shown per page. + * @default 10 + **/ + defaultPageSize?: 10 | 25 | 50; + /** + * The current page. Passing this prop will make the component controlled and will not update the page on its own. + **/ + currentPage?: number; + + /** + * Callback function that is called when the page size is changed + */ + onPageSizeChange?: ({ pageSize }: { pageSize: number }) => void; + /** + * Whether to show the page size picker. It will be always be hidden on mobile. + * Page size picker controls how rows are shown per page. + * @default true + */ + showPageSizePicker?: boolean; + /** + * Whether to show the page number selector. It will be always be hidden on mobile. + * Page number selectors is a group of buttons that allows the user to jump to a specific page. + * @default false + */ + showPageNumberSelector?: boolean; + /** + * Content of the label to be shown in the pagination component + * @default `Showing 1 to ${totalItems} Items` + */ + label?: string; + /** + * Whether to show the label. It will be always be hidden on mobile. + * @default false + */ + showLabel?: boolean; +}; + +type TablePaginationType = 'client' | 'server'; + +type TablePaginationServerProps = TablePaginationCommonProps & { + /** + * Whether the pagination is happening on client or server. + * If the pagination is happening on `client`, the Table component will **divide the data into pages** and show the pages based on the page size. + * If the pagination is happening on `server`, the Table component will **not divide the data into pages and will show all the data**. You will have to fetch data for each page as the page changes and pass it to the Table component. + * When paginationType is `server`, the `onPageChange` & `totalItemCount` props are required. + * @default 'client' + * */ + paginationType?: Extract; + /** + * The total number of possible items in the table. This is used to calculate the total number of pages when pagination is happening on server and not all the data is fetched at once. + */ + totalItemCount: number; + /** + * Callback function that is called when the page is changed + */ + onPageChange: ({ page }: { page: number }) => void; +}; + +type TablePaginationClientProps = TablePaginationCommonProps & { + /** + * Whether the pagination is happening on client or server. + * If the pagination is happening on `client`, the Table component will **divide the data into pages** and show the pages based on the page size. + * If the pagination is happening on `server`, the Table component will **not divide the data into pages and will show all the data**. You will have to fetch data for each page as the page changes and pass it to the Table component. + * When paginationType is `server`, the `onPageChange` & `totalItemCount` props are required. + * @default 'client' + * */ + paginationType?: Extract; + /** + * The total number of possible items in the table. This is used to calculate the total number of pages when pagination is happening on server and not all the data is fetched at once. + */ + totalItemCount?: number; + /** + * Callback function that is called when the page is changed + */ + onPageChange?: ({ page }: { page: number }) => void; +}; + +type TablePaginationProps = TablePaginationCommonProps & + (TablePaginationServerProps | TablePaginationClientProps); + +type TableToolbarProps = { + /** + * The children of TableToolbar should be TableToolbarActions + */ + children?: React.ReactNode; + /** + * The title of the TableToolbar. If not provided, it will show the default title. + * @default `Showing 1 to ${totalItems} Items` + */ + title?: string; + /** + * The title to show when items are selected. If not provided, it will show the default title. + * @default `${selectedRows.length} 'Items'} Selected` + */ + selectedTitle?: string; +}; + +type TableToolbarActionsProps = { + children?: React.ReactNode; +} & StyledPropsBlade; +``` + +## Example + +```jsx +import { + Table, + TableEditableCell, + TableHeader, + TableHeaderRow, + TableHeaderCell, + TableBody, + TableRow, + TableCell, + TableToolbar, + TableToolbarActions, + TableFooter, + TableFooterRow, + TableFooterCell, + TablePagination, +} from '@razorpay/blade/components'; + +const nodes: Item[] = [ + ...Array.from({ length: 100 }, (_, i) => ({ + id: (i + 1).toString(), + paymentId: `rzp${Math.floor(Math.random() * 1000000)}`, + amount: Number((Math.random() * 10000).toFixed(2)), + status: ['Completed', 'Pending', 'Failed'][Math.floor(Math.random() * 3)], + date: new Date(2021, Math.floor(Math.random() * 12), Math.floor(Math.random() * 28) + 1), + type: ['Payout', 'Refund'][Math.floor(Math.random() * 2)], + method: ['Bank Transfer', 'Credit Card', 'PayPal'][Math.floor(Math.random() * 3)], + bank: ['HDFC', 'ICICI', 'SBI'][Math.floor(Math.random() * 3)], + account: Math.floor(Math.random() * 1000000000).toString(), + name: [ + 'John Doe', + 'Jane Doe', + 'Bob Smith', + 'Alice Smith', + 'John Smith', + 'Jane Smith', + 'Bob Doe', + 'Alice Doe', + ][Math.floor(Math.random() * 8)], + })), +]; + +type Item = { + id: string, + paymentId: string, + amount: number, + status: string, + date: Date, + type: string, + method: string, + bank: string, + account: string, + name: string, +}; +const data: TableData = { + nodes, +}; + +const TableComponent: StoryFn = ({ ...args }) => { + return ( + + + + + + + + } + sortFunctions={{ + ID: (array) => array.sort((a, b) => Number(a.id) - Number(b.id)), + AMOUNT: (array) => array.sort((a, b) => a.amount - b.amount), + PAYMENT_ID: (array) => array.sort((a, b) => a.paymentId.localeCompare(b.paymentId)), + DATE: (array) => array.sort((a, b) => a.date.getTime() - b.date.getTime()), + STATUS: (array) => array.sort((a, b) => a.status.localeCompare(b.status)), + }} + pagination={ + + } + > + {/* tableData variables has things that you passed from data.nodes in Table component */} + {(tableData) => ( + <> + {/* Add when you need header */} + + + ID + Amount + Account + Date + Method + Status + + + + {tableData.map((tableItem, index) => ( + + + {tableItem.paymentId} + + + {tableItem.account} + + {tableItem.date?.toLocaleDateString('en-IN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + })} + + {tableItem.method} + + + {tableItem.status} + + + + ))} + + {/* Add when you need footer */} + + + Total + - + - + - + - + {args.selectionType === 'multiple' ? - : null} + + + + + + + )} +
+
+ ); +}; +``` diff --git a/packages/blade/codemods/aicodemod/server.mjs b/packages/blade/codemods/aicodemod/server.mjs new file mode 100644 index 00000000000..cbe57bc765f --- /dev/null +++ b/packages/blade/codemods/aicodemod/server.mjs @@ -0,0 +1,27 @@ +import Fastify from 'fastify'; +import { getChatCompletion } from './getChatCompletion.mjs'; + +const fastify = Fastify(); + +// Declare a route +fastify.post('/chat/completions', async (request, reply) => { + // Access the body of the request + const body = request.body; + + const data = await getChatCompletion(body.messages); + + // Send a response + return data; +}); + +// Run the server +const start = async () => { + try { + await fastify.listen({ port: 3000 }); + fastify.log.info(`Server is running at http://localhost:3000`); + } catch (err) { + fastify.log.error(err); + process.exit(1); + } +}; +start(); diff --git a/packages/blade/package.json b/packages/blade/package.json index 0433f4e12d3..1b12de05e5f 100644 --- a/packages/blade/package.json +++ b/packages/blade/package.json @@ -7,7 +7,8 @@ "node": ">=18.12.1" }, "bin": { - "migrate-typography": "./codemods/migrate-typography/transformers/migrate-typography.ts" + "migrate-typography": "./codemods/migrate-typography/transformers/migrate-typography.ts", + "aicodemod": "./codemods/aicodemod/index.mjs" }, "repository": { "type": "git", @@ -235,6 +236,7 @@ "dedent": "0.7.0", "eslint-plugin-mdx": "1.16.0", "execa": "5.0.0", + "fastify": "4.28.1", "figures": "3.2.0", "flat": "5.0.2", "globby": "14.0.1", diff --git a/yarn.lock b/yarn.lock index e05fad2c51e..3d75288313d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2476,6 +2476,34 @@ resolved "https://registry.yarnpkg.com/@fal-works/esbuild-plugin-global-externals/-/esbuild-plugin-global-externals-2.1.2.tgz#c05ed35ad82df8e6ac616c68b92c2282bd083ba4" integrity sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ== +"@fastify/ajv-compiler@^3.5.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@fastify/ajv-compiler/-/ajv-compiler-3.6.0.tgz#907497a0e62a42b106ce16e279cf5788848e8e79" + integrity sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ== + dependencies: + ajv "^8.11.0" + ajv-formats "^2.1.1" + fast-uri "^2.0.0" + +"@fastify/error@^3.3.0", "@fastify/error@^3.4.0": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@fastify/error/-/error-3.4.1.tgz#b14bb4cac3dd4ec614becbc643d1511331a6425c" + integrity sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ== + +"@fastify/fast-json-stringify-compiler@^4.3.0": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz#5df89fa4d1592cbb8780f78998355feb471646d5" + integrity sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA== + dependencies: + fast-json-stringify "^5.7.0" + +"@fastify/merge-json-schemas@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz#3551857b8a17a24e8c799e9f51795edb07baa0bc" + integrity sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA== + dependencies: + fast-deep-equal "^3.1.3" + "@floating-ui/core@^1.0.0", "@floating-ui/core@^1.2.6": version "1.6.0" resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.0.tgz#fa41b87812a16bf123122bf945946bae3fdf7fc1" @@ -8432,6 +8460,11 @@ absolute-path@^0.0.0: resolved "https://registry.yarnpkg.com/absolute-path/-/absolute-path-0.0.0.tgz#a78762fbdadfb5297be99b15d35a785b2f095bf7" integrity sha512-HQiug4c+/s3WOvEnDRxXVmNtSG5s2gJM9r19BTcqjp7BWcE48PB+Y2G6jE65kqI0LpsQeMZygt/b60Gi4KxGyA== +abstract-logging@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/abstract-logging/-/abstract-logging-2.0.1.tgz#6b0c371df212db7129b57d2e7fcf282b8bf1c839" + integrity sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA== + accepts@^1.3.7, accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -8579,6 +8612,13 @@ ajv-formats@^2.1.1: dependencies: ajv "^8.0.0" +ajv-formats@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-3.0.1.tgz#3d5dc762bca17679c3c2ea7e90ad6b7532309578" + integrity sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ== + dependencies: + ajv "^8.0.0" + ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" @@ -8611,6 +8651,16 @@ ajv@^8.0.0, ajv@^8.0.1, ajv@^8.6.0, ajv@^8.9.0: require-from-string "^2.0.2" uri-js "^4.2.2" +ajv@^8.10.0, ajv@^8.11.0: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + anser@^1.4.9: version "1.4.10" resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.10.tgz#befa3eddf282684bd03b63dcda3927aef8c2e35b" @@ -9171,6 +9221,11 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +atomic-sleep@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" + integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== + autoprefixer@^10.4.13: version "10.4.19" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.19.tgz#ad25a856e82ee9d7898c59583c1afeb3fa65f89f" @@ -9203,6 +9258,14 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" +avvio@^8.3.0: + version "8.3.2" + resolved "https://registry.yarnpkg.com/avvio/-/avvio-8.3.2.tgz#cb5844a612e8421d1f3aef8895ef7fa12f73563f" + integrity sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ== + dependencies: + "@fastify/error" "^3.3.0" + fastq "^1.17.1" + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -11149,7 +11212,7 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@0.6.0: +cookie@0.6.0, cookie@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== @@ -13625,7 +13688,7 @@ eventemitter3@^4.0.0, eventemitter3@^4.0.4: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -events@^3.0.0, events@^3.2.0: +events@^3.0.0, events@^3.2.0, events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== @@ -13909,6 +13972,16 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== +fast-content-type-parse@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz#4087162bf5af3294d4726ff29b334f72e3a1092c" + integrity sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ== + +fast-decode-uri-component@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543" + integrity sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg== + fast-deep-equal@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" @@ -13962,11 +14035,46 @@ fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-sta resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +fast-json-stringify@^5.7.0, fast-json-stringify@^5.8.0: + version "5.16.1" + resolved "https://registry.yarnpkg.com/fast-json-stringify/-/fast-json-stringify-5.16.1.tgz#a6d0c575231a3a08c376a00171d757372f2ca46e" + integrity sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g== + dependencies: + "@fastify/merge-json-schemas" "^0.1.0" + ajv "^8.10.0" + ajv-formats "^3.0.1" + fast-deep-equal "^3.1.3" + fast-uri "^2.1.0" + json-schema-ref-resolver "^1.0.1" + rfdc "^1.2.0" + fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-querystring@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/fast-querystring/-/fast-querystring-1.1.2.tgz#a6d24937b4fc6f791b4ee31dcb6f53aeafb89f53" + integrity sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg== + dependencies: + fast-decode-uri-component "^1.0.1" + +fast-redact@^3.1.1: + version "3.5.0" + resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.5.0.tgz#e9ea02f7e57d0cd8438180083e93077e496285e4" + integrity sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A== + +fast-uri@^2.0.0, fast-uri@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-2.4.0.tgz#67eae6fbbe9f25339d5d3f4c4234787b65d7d55e" + integrity sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA== + +fast-uri@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.1.tgz#cddd2eecfc83a71c1be2cc2ef2061331be8a7134" + integrity sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw== + fast-xml-parser@^4.0.12: version "4.3.6" resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.3.6.tgz#190f9d99097f0c8f2d3a0e681a10404afca052ff" @@ -13979,7 +14087,29 @@ fastest-levenshtein@^1.0.12: resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== -fastq@^1.6.0: +fastify@4.28.1: + version "4.28.1" + resolved "https://registry.yarnpkg.com/fastify/-/fastify-4.28.1.tgz#39626dedf445d702ef03818da33064440b469cd1" + integrity sha512-kFWUtpNr4i7t5vY2EJPCN2KgMVpuqfU4NjnJNCgiNB900oiDeYqaNDRcAfeBbOF5hGixixxcKnOU4KN9z6QncQ== + dependencies: + "@fastify/ajv-compiler" "^3.5.0" + "@fastify/error" "^3.4.0" + "@fastify/fast-json-stringify-compiler" "^4.3.0" + abstract-logging "^2.0.1" + avvio "^8.3.0" + fast-content-type-parse "^1.1.0" + fast-json-stringify "^5.8.0" + find-my-way "^8.0.0" + light-my-request "^5.11.0" + pino "^9.0.0" + process-warning "^3.0.0" + proxy-addr "^2.0.7" + rfdc "^1.3.0" + secure-json-parse "^2.7.0" + semver "^7.5.4" + toad-cache "^3.3.0" + +fastq@^1.17.1, fastq@^1.6.0: version "1.17.1" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== @@ -14174,6 +14304,15 @@ find-file-up@^0.1.2: fs-exists-sync "^0.1.0" resolve-dir "^0.1.0" +find-my-way@^8.0.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/find-my-way/-/find-my-way-8.2.0.tgz#ef1b83d008114a300118c9c707d8dc65947d9960" + integrity sha512-HdWXgFYc6b1BJcOBDBwjqWuHJj1WYiqrxSh25qtU4DabpMFdj/gSunNBQb83t+8Zt67D7CXEzJWTkxaShMTMOA== + dependencies: + fast-deep-equal "^3.1.3" + fast-querystring "^1.0.0" + safe-regex2 "^3.1.0" + find-pkg@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/find-pkg/-/find-pkg-0.1.2.tgz#1bdc22c06e36365532e2a248046854b9788da557" @@ -18212,6 +18351,13 @@ json-parse-even-better-errors@^3.0.0: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz#02bb29fb5da90b5444581749c22cedd3597c6cb0" integrity sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg== +json-schema-ref-resolver@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz#6586f483b76254784fc1d2120f717bdc9f0a99bf" + integrity sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw== + dependencies: + fast-deep-equal "^3.1.3" + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -18544,6 +18690,15 @@ liftoff@^4.0.0: rechoir "^0.8.0" resolve "^1.20.0" +light-my-request@^5.11.0: + version "5.13.0" + resolved "https://registry.yarnpkg.com/light-my-request/-/light-my-request-5.13.0.tgz#b29905e55e8605b77fee2a946e17b219bca35113" + integrity sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ== + dependencies: + cookie "^0.6.0" + process-warning "^3.0.0" + set-cookie-parser "^2.4.1" + lilconfig@^2.0.3, lilconfig@^2.0.6, lilconfig@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" @@ -21379,6 +21534,11 @@ ohash@^1.1.3: resolved "https://registry.yarnpkg.com/ohash/-/ohash-1.1.3.tgz#f12c3c50bfe7271ce3fd1097d42568122ccdcf07" integrity sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw== +on-exit-leak-free@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz#fed195c9ebddb7d9e4c3842f93f281ac8dadd3b8" + integrity sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA== + on-finished@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" @@ -22172,6 +22332,36 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== +pino-abstract-transport@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz#97f9f2631931e242da531b5c66d3079c12c9d1b5" + integrity sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q== + dependencies: + readable-stream "^4.0.0" + split2 "^4.0.0" + +pino-std-serializers@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz#7c625038b13718dbbd84ab446bd673dc52259e3b" + integrity sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA== + +pino@^9.0.0: + version "9.3.2" + resolved "https://registry.yarnpkg.com/pino/-/pino-9.3.2.tgz#a530d6d28f1d954b6f54416a218cbb616f52f901" + integrity sha512-WtARBjgZ7LNEkrGWxMBN/jvlFiE17LTbBoH0konmBU684Kd0uIiDwBXlcTCW7iJnA6HfIKwUssS/2AC6cDEanw== + dependencies: + atomic-sleep "^1.0.0" + fast-redact "^3.1.1" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^1.2.0" + pino-std-serializers "^7.0.0" + process-warning "^4.0.0" + quick-format-unescaped "^4.0.3" + real-require "^0.2.0" + safe-stable-stringify "^2.3.1" + sonic-boom "^4.0.1" + thread-stream "^3.0.0" + pinpoint@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pinpoint/-/pinpoint-1.1.0.tgz#0cf7757a6977f1bf7f6a32207b709e377388e874" @@ -23123,6 +23313,16 @@ process-on-spawn@^1.0.0: dependencies: fromentries "^1.2.0" +process-warning@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-3.0.0.tgz#96e5b88884187a1dce6f5c3166d611132058710b" + integrity sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ== + +process-warning@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-4.0.0.tgz#581e3a7a1fb456c5f4fd239f76bce75897682d5a" + integrity sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw== + process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" @@ -23222,7 +23422,7 @@ protocols@^2.0.1: resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" integrity sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q== -proxy-addr@~2.0.7: +proxy-addr@^2.0.7, proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== @@ -23402,6 +23602,11 @@ queue@6.0.2: dependencies: inherits "~2.0.3" +quick-format-unescaped@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" + integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== + quick-lru@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" @@ -24151,6 +24356,17 @@ readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stre string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^4.0.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.5.2.tgz#9e7fc4c45099baeed934bff6eb97ba6cf2729e09" + integrity sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + readable-stream@~1.0.31: version "1.0.34" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" @@ -24197,6 +24413,11 @@ readline@^1.3.0: resolved "https://registry.yarnpkg.com/readline/-/readline-1.3.0.tgz#c580d77ef2cfc8752b132498060dc9793a7ac01c" integrity sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg== +real-require@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78" + integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg== + recast@^0.20.3, recast@^0.20.4: version "0.20.5" resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.5.tgz#8e2c6c96827a1b339c634dd232957d230553ceae" @@ -24733,6 +24954,11 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +ret@~0.4.0: + version "0.4.3" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.4.3.tgz#5243fa30e704a2e78a9b9b1e86079e15891aa85c" + integrity sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ== + retry@0.12.0, retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" @@ -24748,6 +24974,11 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rfdc@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + rfdc@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.1.tgz#2b6d4df52dffe8bb346992a10ea9451f24373a8f" @@ -24909,6 +25140,13 @@ safe-regex-test@^1.0.3: es-errors "^1.3.0" is-regex "^1.1.4" +safe-regex2@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/safe-regex2/-/safe-regex2-3.1.0.tgz#fd7ec23908e2c730e1ce7359a5b72883a87d2763" + integrity sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug== + dependencies: + ret "~0.4.0" + safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -24916,6 +25154,11 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" +safe-stable-stringify@^2.3.1: + version "2.4.3" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -25033,6 +25276,11 @@ secure-compare@3.0.1: resolved "https://registry.yarnpkg.com/secure-compare/-/secure-compare-3.0.1.tgz#f1a0329b308b221fae37b9974f3d578d0ca999e3" integrity sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw== +secure-json-parse@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" + integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== + select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -25178,6 +25426,11 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-cookie-parser@^2.4.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz#131921e50f62ff1a66a461d7d62d7b21d5d15a51" + integrity sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ== + set-function-length@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" @@ -25483,6 +25736,13 @@ socks@^2.3.3, socks@^2.6.2: ip-address "^9.0.5" smart-buffer "^4.2.0" +sonic-boom@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-4.0.1.tgz#515b7cef2c9290cb362c4536388ddeece07aed30" + integrity sha512-hTSD/6JMLyT4r9zeof6UtuBDpjJ9sO08/nmS5djaA9eozT9oOlNdpXSnzcgj4FTqpk3nkLrs61l4gip9r1HCrQ== + dependencies: + atomic-sleep "^1.0.0" + sort-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" @@ -25691,6 +25951,11 @@ split2@^3.0.0: dependencies: readable-stream "^3.0.0" +split2@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" + integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== + split@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" @@ -25894,7 +26159,7 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -25912,15 +26177,6 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -25996,7 +26252,7 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -string_decoder@^1.0.0, string_decoder@^1.1.1: +string_decoder@^1.0.0, string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -26041,7 +26297,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -26062,13 +26318,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -26762,6 +27011,13 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" +thread-stream@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-3.1.0.tgz#4b2ef252a7c215064507d4ef70c05a5e2d34c4f1" + integrity sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A== + dependencies: + real-require "^0.2.0" + throat@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" @@ -26888,6 +27144,11 @@ to-vfile@^7.0.0: is-buffer "^2.0.0" vfile "^5.1.0" +toad-cache@^3.3.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/toad-cache/-/toad-cache-3.7.0.tgz#b9b63304ea7c45ec34d91f1d2fa513517025c441" + integrity sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw== + tocbot@^4.20.1: version "4.25.0" resolved "https://registry.yarnpkg.com/tocbot/-/tocbot-4.25.0.tgz#bc38aea5ec8f076779bb39636f431b044129a237" @@ -28724,7 +28985,7 @@ worker-rpc@^0.1.0: dependencies: microevent.ts "~0.1.1" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -28742,15 +29003,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From ff8535df295cf2602627098ed2fc1e96e809525b Mon Sep 17 00:00:00 2001 From: saurabhdaware Date: Fri, 2 Aug 2024 09:22:42 +0530 Subject: [PATCH 2/8] refactor: server and bin code --- .../codemods/aicodemod/{index.mjs => bin.mjs} | 0 packages/blade/codemods/aicodemod/config.mjs | 7 ------- .../codemods/aicodemod/getChatCompletion.mjs | 17 +++++++++-------- packages/blade/codemods/aicodemod/server.mjs | 14 +++++++++++++- packages/blade/package.json | 2 +- 5 files changed, 23 insertions(+), 17 deletions(-) rename packages/blade/codemods/aicodemod/{index.mjs => bin.mjs} (100%) delete mode 100644 packages/blade/codemods/aicodemod/config.mjs diff --git a/packages/blade/codemods/aicodemod/index.mjs b/packages/blade/codemods/aicodemod/bin.mjs similarity index 100% rename from packages/blade/codemods/aicodemod/index.mjs rename to packages/blade/codemods/aicodemod/bin.mjs diff --git a/packages/blade/codemods/aicodemod/config.mjs b/packages/blade/codemods/aicodemod/config.mjs deleted file mode 100644 index a53942b72f8..00000000000 --- a/packages/blade/codemods/aicodemod/config.mjs +++ /dev/null @@ -1,7 +0,0 @@ -export const aiConfig = { - temperature: 0.7, - top_p: 0.95, - max_tokens: 1000, - // Doesn't make actual API call when set to true. Instead prints the input itself - testMode: true, -}; diff --git a/packages/blade/codemods/aicodemod/getChatCompletion.mjs b/packages/blade/codemods/aicodemod/getChatCompletion.mjs index 4e0defe810c..f448ad85dbd 100644 --- a/packages/blade/codemods/aicodemod/getChatCompletion.mjs +++ b/packages/blade/codemods/aicodemod/getChatCompletion.mjs @@ -1,14 +1,16 @@ -import { aiConfig } from './config.mjs'; +const aiConfig = { + temperature: 0.7, + top_p: 0.95, + max_tokens: 1000, +}; const getChatCompletion = async (messages) => { - const { testMode, ...payloadConfig } = aiConfig; - - if (testMode) { + if (process.env.TEST_MODE === 'true') { console.log('messages:'); console.dir(messages, { depth: Infinity }); return { answer: - 'The server is running in testMode. Its not supposed to. Blame @Saurabh for this on slack #design-system channel.', + 'The server is running in TEST_MODE environment. Its not supposed to. Blame @Saurabh for this on slack #design-system channel.', usage: { total_tokens: 0 }, inputLength: JSON.stringify(messages).length, }; @@ -22,11 +24,10 @@ const getChatCompletion = async (messages) => { // Payload for the request const payload = { messages, - ...payloadConfig, + ...aiConfig, }; - const GPT4V_ENDPOINT = - 'https://design-system-blade.openai.azure.com/openai/deployments/Designsystem/chat/completions?api-version=2024-02-15-preview'; + const GPT4V_ENDPOINT = `${process.env.OPENAI_BASE_URL}/chat/completions?api-version=2024-02-15-preview`; // Send request let data; diff --git a/packages/blade/codemods/aicodemod/server.mjs b/packages/blade/codemods/aicodemod/server.mjs index cbe57bc765f..fd4e2e69d18 100644 --- a/packages/blade/codemods/aicodemod/server.mjs +++ b/packages/blade/codemods/aicodemod/server.mjs @@ -1,7 +1,19 @@ +import fs from 'fs'; +import path from 'path'; +import dotenv from 'dotenv'; import Fastify from 'fastify'; import { getChatCompletion } from './getChatCompletion.mjs'; -const fastify = Fastify(); +const __dirname = new URL('.', import.meta.url).pathname; +const envFilePath = path.resolve(__dirname, '../../.env.local'); + +if (!fs.existsSync(envFilePath)) { + throw new Error('.env.local does not exist in blade package root'); +} + +dotenv.config({ path: envFilePath }); + +const fastify = Fastify({ logger: true }); // Declare a route fastify.post('/chat/completions', async (request, reply) => { diff --git a/packages/blade/package.json b/packages/blade/package.json index 1b12de05e5f..7b28caa7c32 100644 --- a/packages/blade/package.json +++ b/packages/blade/package.json @@ -8,7 +8,7 @@ }, "bin": { "migrate-typography": "./codemods/migrate-typography/transformers/migrate-typography.ts", - "aicodemod": "./codemods/aicodemod/index.mjs" + "aicodemod": "./codemods/aicodemod/bin.mjs" }, "repository": { "type": "git", From e9b0c872ef72ee8294798c0f475b8157e43f464a Mon Sep 17 00:00:00 2001 From: saurabhdaware Date: Mon, 5 Aug 2024 10:10:16 +0530 Subject: [PATCH 3/8] feat: add example migration of table --- packages/blade/codemods/aicodemod/bin.mjs | 54 ++++- .../codemods/aicodemod/getChatCompletion.mjs | 6 +- .../codemods/aicodemod/sampleMigration.mjs | 191 ++++++++++++++++++ 3 files changed, 238 insertions(+), 13 deletions(-) create mode 100644 packages/blade/codemods/aicodemod/sampleMigration.mjs diff --git a/packages/blade/codemods/aicodemod/bin.mjs b/packages/blade/codemods/aicodemod/bin.mjs index b26321ee9d2..55bae1ba8fa 100755 --- a/packages/blade/codemods/aicodemod/bin.mjs +++ b/packages/blade/codemods/aicodemod/bin.mjs @@ -1,13 +1,23 @@ #!/usr/bin/env node import fs from 'fs'; import path from 'path'; +import { migrated, original } from './sampleMigration.mjs'; -// This is our own server from where we call openai -const SERVER_BASE_URL = ''; +const getFlagValue = (flag) => { + const flagIndex = process.argv.indexOf(flag); + if (flagIndex < 0) { + return 'http://localhost:3000'; + } + + return process.argv[flagIndex + 1]; +}; + +// This is for passing ngrok instance URL +const baseUrl = getFlagValue('--base-url'); const SERVER_ENDPOINT = '/chat/completions'; const getChatCompletionFromServer = async (messages) => { - const url = SERVER_BASE_URL + SERVER_ENDPOINT; + const url = baseUrl + SERVER_ENDPOINT; // Define the fetch options const options = { @@ -39,24 +49,40 @@ I want you to migrate our old codebase code from custom implementation, to use o You fill find code snippets of these things below- 1. Custom Component Definitions: Custom component implementations to help figure out what they do -4. Blade's Table Documentation: Documentation of our design-system table -3. Current Usage: Usage of the custom implimented component +2. Blade's Table Documentation: Documentation of our design-system table +3. Example Migration for Reference: Understand and use this as a reference for any migrations +4. Code to Migrate: Migrate this code to use our design-system components Return Format: - Refer to Custom component definitions, and Blade's Table documentation and return the migrated code snippet of custom usage using Blade -- Do not add any additional text +- Return raw migrated code without additional text - Don't change unrelated code and imports +- Remove variables that are no longer in use - Make sure Blade's Header and Footer are also used wherever needed. -- Ignore the code related to responsiveness as Blade components are responsive by default +- Ignore the code related to mobile responsiveness as Blade components are responsive by default -## Custom Component Definitions +## 1. Custom Component Definitions ${codeKnowledge} -## Blade's Table Documentation +## 2. Blade's Table Documentation ${bladeKnowledge} -## Current Usage +## 3. Example Migration for Reference + +### Old Custom Table Code +\`\`\`jsx +${original} +\`\`\` + +### Migrated Table Code +\`\`\`jsx +${migrated} +\`\`\` + +## 4. Code to Migrate +\`\`\`jsx ${usage} +\`\`\` `; const messages = [ @@ -80,5 +106,11 @@ const data = await getChatCompletionFromServer(messages); console.log('USAGE STATS', { inputLength: data.inputLength, openAIUsage: data.usage, + v: 5, }); -fs.writeFileSync(usageFilePath, data.answer.content); + +const out = data.answer.content; +const cleanOut = out.startsWith('```jsx') + ? out.slice(`\`\`\`jsx`.length + 1, -('```'.length + 1)) + : out; +fs.writeFileSync(usageFilePath, cleanOut); diff --git a/packages/blade/codemods/aicodemod/getChatCompletion.mjs b/packages/blade/codemods/aicodemod/getChatCompletion.mjs index f448ad85dbd..46d4cd0a6f4 100644 --- a/packages/blade/codemods/aicodemod/getChatCompletion.mjs +++ b/packages/blade/codemods/aicodemod/getChatCompletion.mjs @@ -9,8 +9,10 @@ const getChatCompletion = async (messages) => { console.log('messages:'); console.dir(messages, { depth: Infinity }); return { - answer: - 'The server is running in TEST_MODE environment. Its not supposed to. Blame @Saurabh for this on slack #design-system channel.', + answer: { + content: + 'The server is running in TEST_MODE environment. Its not supposed to. Blame @Saurabh for this on slack #design-system channel.', + }, usage: { total_tokens: 0 }, inputLength: JSON.stringify(messages).length, }; diff --git a/packages/blade/codemods/aicodemod/sampleMigration.mjs b/packages/blade/codemods/aicodemod/sampleMigration.mjs new file mode 100644 index 00000000000..5e7e99faae8 --- /dev/null +++ b/packages/blade/codemods/aicodemod/sampleMigration.mjs @@ -0,0 +1,191 @@ +export const original = `import Text from '@razorpay/blade-old/src/atoms/Text'; +import View from '@razorpay/blade-old/src/atoms/View'; +import Flex from '@razorpay/blade-old/src/atoms/Flex'; +import Icon from '@razorpay/blade-old/src/atoms/Icon'; + +import TableBody from 'common/ui/TableBody'; +import MobileListView from 'common/ui/MobileListView'; +import { amount, createdAt } from 'common/ui/item/pair'; + +import EntityItemRow from 'merchant/containers/EntityItemRow'; +import { isMobileDevice } from 'merchant/components/Home/data'; +import { + customer, + invoiceId, + invoiveStatus, + paymentLink, + paymentLinkId, + receiptNumber, + referenceId, +} from 'merchant/views/Invoices/Invoices/item'; +import { shareURL } from 'merchant/views/Invoices/Invoices/helpers'; + +const PaymentLinkMobileTableListView = (items) => { + const { item, onShareLinkSuccess = () => {} } = items; + + return ( + + + {paymentLinkId.value(item)} + + {item?.customer_details && customer.value(item)} + + {item?.short_url && ( + { + onShareLinkSuccess(); + shareURL(item.short_url, item?.id); + }} + > + + +
+ +
+ Share payment link +
+
+
+ )} + + + {amount.value(item)} + {invoiveStatus.value(item)} +
+ ); +}; + +const InvoiceListItem = (props) => { + const { invoice, columns } = props; + + return ( + + {columns.map(({ title, value }) => ( + {value(invoice)} + ))} + + ); +}; + +export default (props) => { + const { + type, + invoices, + isLoading, + onCopy = () => {}, + EmptyList, + isPaymentlinksV2Enabled, + } = props; + + const isPaymentLinksType = type === 'link'; + const label = isPaymentLinksType ? paymentLinkId : invoiceId; + const listStyle = isPaymentLinksType ? 'Payment-link-list' : 'Invoice-list'; + const receipt = isPaymentlinksV2Enabled ? referenceId : receiptNumber; + + const columns = [label, createdAt, amount, receipt, customer, paymentLink(onCopy), invoiveStatus]; + + return isMobileDevice() && isPaymentLinksType ? ( + + ) : ( +
+ + + + {columns.map(({ title }) => ( + + ))} + + + + {invoices.map((invoice) => ( + + ))} + +
{title}
+
+ ); +}; +`; + +export const migrated = ` +import { + Table, + TableHeader, + TableHeaderRow, + TableHeaderCell, + TableBody, + TableRow, + TableCell, +} from '@razorpay/blade/components'; + +import { amount, createdAt } from 'common/ui/item/pair'; + +import { + customer, + invoiceId, + invoiveStatus, + paymentLink, + paymentLinkId, + receiptNumber, + referenceId, +} from 'merchant/views/Invoices/Invoices/item'; +import { shareURL } from 'merchant/views/Invoices/Invoices/helpers'; + +const InvoiceListItem = (props) => { + const { invoice, columns } = props; + + return ( + + {columns.map(({ title, value }) => ( + {value(invoice)} + ))} + + ); +}; + +export default (props) => { + const { + type, + invoices, + isLoading, + onCopy = () => {}, + EmptyList, + isPaymentlinksV2Enabled, + } = props; + + const isPaymentLinksType = type === 'link'; + const label = isPaymentLinksType ? paymentLinkId : invoiceId; + const receipt = isPaymentlinksV2Enabled ? referenceId : receiptNumber; + + const columns = [label, createdAt, amount, receipt, customer, paymentLink(onCopy), invoiveStatus]; + + return ( + + {(tableData) => ( + <> + + + {columns.map(({ title }) => ( + {title} + ))} + + + + {tableData.map((invoice) => ( + + ))} + + + )} +
+ ); +}; +`; From 3dd7243dcb5fa16c3c50c7e2c9665c84d50d0d19 Mon Sep 17 00:00:00 2001 From: saurabhdaware Date: Tue, 6 Aug 2024 12:51:43 +0530 Subject: [PATCH 4/8] feat: add preset to repo --- packages/blade/codemods/aicodemod/bin.mjs | 66 ++-- .../codemods/aicodemod/getChatCompletion.mjs | 2 +- .../presets/dashboard/table-pattern-1.md | 285 ++++++++++++++++++ 3 files changed, 325 insertions(+), 28 deletions(-) create mode 100644 packages/blade/codemods/aicodemod/knowledge/presets/dashboard/table-pattern-1.md diff --git a/packages/blade/codemods/aicodemod/bin.mjs b/packages/blade/codemods/aicodemod/bin.mjs index 55bae1ba8fa..924dabc345d 100755 --- a/packages/blade/codemods/aicodemod/bin.mjs +++ b/packages/blade/codemods/aicodemod/bin.mjs @@ -6,14 +6,16 @@ import { migrated, original } from './sampleMigration.mjs'; const getFlagValue = (flag) => { const flagIndex = process.argv.indexOf(flag); if (flagIndex < 0) { - return 'http://localhost:3000'; + return undefined; } return process.argv[flagIndex + 1]; }; // This is for passing ngrok instance URL -const baseUrl = getFlagValue('--base-url'); +const baseUrl = getFlagValue('--base-url') ?? 'http://localhost:3000'; +const preset = getFlagValue('--code-knowledge') ?? 'presets/dashboard/table-pattern-1'; + const SERVER_ENDPOINT = '/chat/completions'; const getChatCompletionFromServer = async (messages) => { @@ -28,15 +30,35 @@ const getChatCompletionFromServer = async (messages) => { body: JSON.stringify({ messages }), }; - // Make the fetch call - const response = await fetch(url, options); - return response.json(); + try { + // Make the fetch call + const response = await fetch(url, options); + return response.json(); + } catch (err) { + if (err.cause.code === 'ECONNREFUSED') { + console.log( + ">> Looks like the Blade OpenAI POC Server is not accessible on network. Tag @Saurabh on slack in #design-system channel so he can start it. P.S. This is just until POC since we don't have a dedicated server yet 🙈", + ); + process.exit(1); + } else { + throw err; + } + } }; const __dirname = new URL('.', import.meta.url).pathname; const cwd = process.cwd(); -const codeKnowledge = fs.readFileSync(path.join(cwd, 'codeKnowledge.md'), 'utf-8'); +let codeKnowledge; +const consumerPresetPath = path.join(cwd, preset); +const predefinedPresetPath = path.join(__dirname, 'knowledge', `${preset}.md`); + +if (fs.existsSync(consumerPresetPath)) { + codeKnowledge = fs.readFileSync(consumerPresetPath, 'utf-8'); +} else { + codeKnowledge = fs.readFileSync(predefinedPresetPath, 'utf-8'); +} + const bladeKnowledge = fs.readFileSync(path.resolve(__dirname, './knowledge/Table.md'), 'utf-8'); const usageFileArg = process.argv[2]; @@ -48,38 +70,28 @@ I want you to migrate our old codebase code from custom implementation, to use o You fill find code snippets of these things below- -1. Custom Component Definitions: Custom component implementations to help figure out what they do -2. Blade's Table Documentation: Documentation of our design-system table -3. Example Migration for Reference: Understand and use this as a reference for any migrations -4. Code to Migrate: Migrate this code to use our design-system components +1. Blade's Table Documentation: Documentation of our design-system table +2. Custom Component Definitions and Migration Patterns: + - Custom component implementations to help figure out what they do + - Migration patterns for reference to help you use this as reference for migrations +3. Code to Migrate: Migrate this code to use our design-system components Return Format: -- Refer to Custom component definitions, and Blade's Table documentation and return the migrated code snippet of custom usage using Blade +- Refer to Blade's Table documentation and Custom Component Definitons and Migration Patterns and return the migrated code snippet of usage using Blade - Return raw migrated code without additional text - Don't change unrelated code and imports - Remove variables that are no longer in use - Make sure Blade's Header and Footer are also used wherever needed. - Ignore the code related to mobile responsiveness as Blade components are responsive by default -## 1. Custom Component Definitions -${codeKnowledge} -## 2. Blade's Table Documentation +## 1. Blade's Table Documentation ${bladeKnowledge} -## 3. Example Migration for Reference - -### Old Custom Table Code -\`\`\`jsx -${original} -\`\`\` - -### Migrated Table Code -\`\`\`jsx -${migrated} -\`\`\` +## 2. Custom Component Definitions and Migration Patterns +${codeKnowledge} -## 4. Code to Migrate +## 3. Code to Migrate \`\`\`jsx ${usage} \`\`\` @@ -106,7 +118,7 @@ const data = await getChatCompletionFromServer(messages); console.log('USAGE STATS', { inputLength: data.inputLength, openAIUsage: data.usage, - v: 5, + v: 7, }); const out = data.answer.content; diff --git a/packages/blade/codemods/aicodemod/getChatCompletion.mjs b/packages/blade/codemods/aicodemod/getChatCompletion.mjs index 46d4cd0a6f4..f9a61cfaadd 100644 --- a/packages/blade/codemods/aicodemod/getChatCompletion.mjs +++ b/packages/blade/codemods/aicodemod/getChatCompletion.mjs @@ -1,7 +1,7 @@ const aiConfig = { temperature: 0.7, top_p: 0.95, - max_tokens: 1000, + max_tokens: 3000, }; const getChatCompletion = async (messages) => { diff --git a/packages/blade/codemods/aicodemod/knowledge/presets/dashboard/table-pattern-1.md b/packages/blade/codemods/aicodemod/knowledge/presets/dashboard/table-pattern-1.md new file mode 100644 index 00000000000..e5c464272d4 --- /dev/null +++ b/packages/blade/codemods/aicodemod/knowledge/presets/dashboard/table-pattern-1.md @@ -0,0 +1,285 @@ +### Codebase Knowledge + +#### EntityItemRow Component + +```jsx +import { Component } from 'react'; +import { connect } from 'react-redux'; + +class EntityItemRow extends Component { + render() { + const { + id, + luminateRowId, + activeEntityId, + activeSecEntityId, + rowClasses = '', + onRowClick, + item, + isDisabled, + } = this.props; + const receiverType = item?.receiver_type || ''; + const sourceChannel = item?.source_channel || ''; + return ( + { + onRowClick?.({ + id, + rowData: { receiverType, sourceChannel }, + }); + }} + className={`${luminateRowId === id ? 'luminate' : ''}${ + activeEntityId === id || activeSecEntityId === id ? ' active' : '' + }${rowClasses ?? ''}${isDisabled?.(item) ? ' disabled' : ''}`} + data-testid={`entity-item-row-${id}`} + > + {this.props.children} + + ); + } +} + +const mapStateToProps = (state) => { + return state.app; +}; + +export default connect(mapStateToProps, null)(EntityItemRow); +``` + +#### TableBody Component + +```jsx +import React from 'react'; +import TableLoader from 'common/ui/TableLoader'; +import EmptyTableRow from 'common/ui/EmptyTableRow'; + +export default (props) => { + let tableRowComponent; + const { + isLoading, + emptyTableRow, + emptyTableMsg, + colSpan, + rows, + children, + SpinnerComponent, + } = props; + + if (isLoading) { + tableRowComponent = SpinnerComponent || ; + } else if (!rows.length) { + tableRowComponent = emptyTableRow || ( + + ); + } + + return ( + + {tableRowComponent + ? typeof tableRowComponent === 'function' + ? tableRowComponent(colSpan) + : tableRowComponent + : children} + + ); +}; +``` + +### Migration Patterns + +#### Original Custom Implimentation Code + +```jsx +import Text from '@razorpay/blade-old/src/atoms/Text'; +import View from '@razorpay/blade-old/src/atoms/View'; +import Flex from '@razorpay/blade-old/src/atoms/Flex'; +import Icon from '@razorpay/blade-old/src/atoms/Icon'; + +import TableBody from 'common/ui/TableBody'; +import MobileListView from 'common/ui/MobileListView'; +import { amount, createdAt } from 'common/ui/item/pair'; + +import EntityItemRow from 'merchant/containers/EntityItemRow'; +import { isMobileDevice } from 'merchant/components/Home/data'; +import { + customer, + invoiceId, + invoiveStatus, + paymentLink, + paymentLinkId, + receiptNumber, + referenceId, +} from 'merchant/views/Invoices/Invoices/item'; +import { shareURL } from 'merchant/views/Invoices/Invoices/helpers'; + +const PaymentLinkMobileTableListView = (items) => { + const { item, onShareLinkSuccess = () => {} } = items; + + return ( + + + {paymentLinkId.value(item)} + + {item?.customer_details && customer.value(item)} + + {item?.short_url && ( + { + onShareLinkSuccess(); + shareURL(item.short_url, item?.id); + }} + > + + +
+ +
+ Share payment link +
+
+
+ )} + + + {amount.value(item)} + {invoiveStatus.value(item)} +
+ ); +}; + +const InvoiceListItem = (props) => { + const { invoice, columns } = props; + + return ( + + {columns.map(({ title, value }) => ( + {value(invoice)} + ))} + + ); +}; + +export default (props) => { + const { + type, + invoices, + isLoading, + onCopy = () => {}, + EmptyList, + isPaymentlinksV2Enabled, + } = props; + + const isPaymentLinksType = type === 'link'; + const label = isPaymentLinksType ? paymentLinkId : invoiceId; + const listStyle = isPaymentLinksType ? 'Payment-link-list' : 'Invoice-list'; + const receipt = isPaymentlinksV2Enabled ? referenceId : receiptNumber; + + const columns = [label, createdAt, amount, receipt, customer, paymentLink(onCopy), invoiveStatus]; + + return isMobileDevice() && isPaymentLinksType ? ( + + ) : ( +
+ + + + {columns.map(({ title }) => ( + + ))} + + + + {invoices.map((invoice) => ( + + ))} + +
{title}
+
+ ); +}; +``` + +#### Expected Code after Migration + +```jsx +import { + Table, + TableHeader, + TableHeaderRow, + TableHeaderCell, + TableBody, + TableRow, + TableCell, +} from '@razorpay/blade/components'; + +import { amount, createdAt } from 'common/ui/item/pair'; + +import { + customer, + invoiceId, + invoiveStatus, + paymentLink, + paymentLinkId, + receiptNumber, + referenceId, +} from 'merchant/views/Invoices/Invoices/item'; +import { shareURL } from 'merchant/views/Invoices/Invoices/helpers'; + +const InvoiceListItem = (props) => { + const { invoice, columns } = props; + + return ( + + {columns.map(({ title, value }) => ( + {value(invoice)} + ))} + + ); +}; + +export default (props) => { + const { + type, + invoices, + isLoading, + onCopy = () => {}, + EmptyList, + isPaymentlinksV2Enabled, + } = props; + + const isPaymentLinksType = type === 'link'; + const label = isPaymentLinksType ? paymentLinkId : invoiceId; + const receipt = isPaymentlinksV2Enabled ? referenceId : receiptNumber; + + const columns = [label, createdAt, amount, receipt, customer, paymentLink(onCopy), invoiveStatus]; + + return ( + + {(tableData) => ( + <> + + + {columns.map(({ title }) => ( + {title} + ))} + + + + {tableData.map((invoice) => ( + + ))} + + + )} +
+ ); +}; +``` From 4b8b5d331ea27fb5c5c6306daae748138ec386c6 Mon Sep 17 00:00:00 2001 From: saurabhdaware Date: Tue, 6 Aug 2024 15:57:03 +0530 Subject: [PATCH 5/8] feat: add docs and presets for aicodemod --- package.json | 4 ++- packages/blade/codemods/aicodemod/README.md | 26 +++++++++++++++++++ packages/blade/codemods/aicodemod/bin.mjs | 18 ++++++------- .../presets/dashboard/table-pattern-1.md | 4 +-- 4 files changed, 39 insertions(+), 13 deletions(-) create mode 100644 packages/blade/codemods/aicodemod/README.md diff --git a/package.json b/package.json index c0318e64165..ffded991e0f 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,9 @@ "publish-npm": "lerna run --scope @razorpay/blade publish-npm", "release": "lerna run --scope @razorpay/blade generate-github-npmrc && changeset publish", "build": "lerna run --scope @razorpay/blade build", - "postinstall": "lerna run --scope eslint-plugin-blade build" + "postinstall": "lerna run --scope eslint-plugin-blade build", + "aicodemod:serve": "node packages/blade/codemods/aicodemod/server.mjs", + "aicodemod:host": "npx localtunnel --port 3000 --subdomain blade-ds" }, "dependencies": {}, "devDependencies": { diff --git a/packages/blade/codemods/aicodemod/README.md b/packages/blade/codemods/aicodemod/README.md new file mode 100644 index 00000000000..cbac1133dca --- /dev/null +++ b/packages/blade/codemods/aicodemod/README.md @@ -0,0 +1,26 @@ +# Blade AI Codemod + +## 1. Install Blade Globally + +```sh +npm install -g @razorpay/blade +``` + +## 2. Run `aicodemod` Command to Migrate + +```sh +aicodemod + --code-knowledge presets/dashboard/table-pattern-1 +``` + +`--code-knowledge`: The AI needs some info about existing codebase and some patterns on how to migrate. We currently have created a preset knowledge for migrating one of the patterns in table. + +## 3. Fix any visual or syntax issues if there + +AI Codemod is there to help you get going with migration. You might still have 10% work left to review if the code logic is in place and visually things are looking as expected. + +> [!NOTE] +> +> **POC Limitations** +> +> As currently its just POC, its optimized to only migrate to Blade Table component. It can also diff --git a/packages/blade/codemods/aicodemod/bin.mjs b/packages/blade/codemods/aicodemod/bin.mjs index 924dabc345d..f1a4efbc211 100755 --- a/packages/blade/codemods/aicodemod/bin.mjs +++ b/packages/blade/codemods/aicodemod/bin.mjs @@ -13,7 +13,7 @@ const getFlagValue = (flag) => { }; // This is for passing ngrok instance URL -const baseUrl = getFlagValue('--base-url') ?? 'http://localhost:3000'; +const baseUrl = getFlagValue('--base-url') ?? 'https://blade-ds.loca.lt'; const preset = getFlagValue('--code-knowledge') ?? 'presets/dashboard/table-pattern-1'; const SERVER_ENDPOINT = '/chat/completions'; @@ -71,16 +71,15 @@ I want you to migrate our old codebase code from custom implementation, to use o You fill find code snippets of these things below- 1. Blade's Table Documentation: Documentation of our design-system table -2. Custom Component Definitions and Migration Patterns: - - Custom component implementations to help figure out what they do - - Migration patterns for reference to help you use this as reference for migrations -3. Code to Migrate: Migrate this code to use our design-system components +2. Custom Component Definitions: Custom component implementations to help figure out what some of the internally used components do +3. Migration Example: A reference example of migration. How the output migrated code would look like on given certain input. +4. Code to Migrate: Migrate this code to use our design-system components Return Format: -- Refer to Blade's Table documentation and Custom Component Definitons and Migration Patterns and return the migrated code snippet of usage using Blade +- Refer to Blade's Table documentation and Custom Component Definitons and Migration Example and return the migrated code snippet of usage using Blade - Return raw migrated code without additional text - Don't change unrelated code and imports -- Remove variables that are no longer in use +- Remove unused variables, functions, and imports - Make sure Blade's Header and Footer are also used wherever needed. - Ignore the code related to mobile responsiveness as Blade components are responsive by default @@ -88,10 +87,9 @@ Return Format: ## 1. Blade's Table Documentation ${bladeKnowledge} -## 2. Custom Component Definitions and Migration Patterns ${codeKnowledge} -## 3. Code to Migrate +## 4. Code to Migrate \`\`\`jsx ${usage} \`\`\` @@ -118,7 +116,7 @@ const data = await getChatCompletionFromServer(messages); console.log('USAGE STATS', { inputLength: data.inputLength, openAIUsage: data.usage, - v: 7, + v: 9, }); const out = data.answer.content; diff --git a/packages/blade/codemods/aicodemod/knowledge/presets/dashboard/table-pattern-1.md b/packages/blade/codemods/aicodemod/knowledge/presets/dashboard/table-pattern-1.md index e5c464272d4..cab60814744 100644 --- a/packages/blade/codemods/aicodemod/knowledge/presets/dashboard/table-pattern-1.md +++ b/packages/blade/codemods/aicodemod/knowledge/presets/dashboard/table-pattern-1.md @@ -1,4 +1,4 @@ -### Codebase Knowledge +### 2. Custom Component Definitions #### EntityItemRow Component @@ -85,7 +85,7 @@ export default (props) => { }; ``` -### Migration Patterns +### 3. Migration Example #### Original Custom Implimentation Code From 27a686bbd9298fc5bc7db137dbde97190335cd38 Mon Sep 17 00:00:00 2001 From: saurabhdaware Date: Wed, 7 Aug 2024 10:30:29 +0530 Subject: [PATCH 6/8] feat: add error handling --- packages/blade/codemods/aicodemod/bin.mjs | 35 ++++++++++++------- .../codemods/aicodemod/getChatCompletion.mjs | 23 +++++++++++- .../presets/dashboard/table-pattern-1.md | 30 ++++++++++++++++ 3 files changed, 75 insertions(+), 13 deletions(-) diff --git a/packages/blade/codemods/aicodemod/bin.mjs b/packages/blade/codemods/aicodemod/bin.mjs index f1a4efbc211..e4efe9b0f7b 100755 --- a/packages/blade/codemods/aicodemod/bin.mjs +++ b/packages/blade/codemods/aicodemod/bin.mjs @@ -108,19 +108,30 @@ const messages = [ }, { role: 'user', - content: ultimatePrompt, + content: [ + { + type: 'text', + text: ultimatePrompt, + }, + ], }, ]; const data = await getChatCompletionFromServer(messages); -console.log('USAGE STATS', { - inputLength: data.inputLength, - openAIUsage: data.usage, - v: 9, -}); - -const out = data.answer.content; -const cleanOut = out.startsWith('```jsx') - ? out.slice(`\`\`\`jsx`.length + 1, -('```'.length + 1)) - : out; -fs.writeFileSync(usageFilePath, cleanOut); + +if (data.answer) { + console.log('USAGE STATS', { + inputLength: data.inputLength, + openAIUsage: data.usage, + v: 9, + }); + + const out = data.answer.content; + const cleanOut = out.startsWith('```jsx') + ? out.slice(`\`\`\`jsx`.length + 1, -('```'.length + 1)) + : out; + fs.writeFileSync(usageFilePath, cleanOut); +} else { + console.log('status=', data.status); + console.error(data.error); +} diff --git a/packages/blade/codemods/aicodemod/getChatCompletion.mjs b/packages/blade/codemods/aicodemod/getChatCompletion.mjs index f9a61cfaadd..8f5af3058fd 100644 --- a/packages/blade/codemods/aicodemod/getChatCompletion.mjs +++ b/packages/blade/codemods/aicodemod/getChatCompletion.mjs @@ -40,16 +40,37 @@ const getChatCompletion = async (messages) => { body: JSON.stringify(payload), }).then((response) => { if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); + console.error(`HTTP error! Status: ${response.status}`); + return { + success: false, + status: response.status, + error: 'HTTP error!', + }; } + return response.json(); }); } catch (error) { console.error(`Failed to make the request. Error: ${error}`); + return { + success: false, + status: 500, + error, + }; } console.log('>> USAGE', data.model, data.usage); + if (data.status === false) { + return { + answer: undefined, + usage: undefined, + inputLength: JSON.stringify(messages).length, + error: data.error, + status: data.status, + }; + } + return { answer: data.choices[0].message, usage: data.usage, diff --git a/packages/blade/codemods/aicodemod/knowledge/presets/dashboard/table-pattern-1.md b/packages/blade/codemods/aicodemod/knowledge/presets/dashboard/table-pattern-1.md index cab60814744..692b029d6a9 100644 --- a/packages/blade/codemods/aicodemod/knowledge/presets/dashboard/table-pattern-1.md +++ b/packages/blade/codemods/aicodemod/knowledge/presets/dashboard/table-pattern-1.md @@ -85,6 +85,36 @@ export default (props) => { }; ``` +#### ShowWhen Component + +```jsx +export const ShowWhen = (props) => { + const i18 = useI18Service(); + // creating an abstraction of just consuming splitz experiment, rest service should not be accessed via RouteGuard + const { abExperiments } = useSplitzService(); + + const { loader, children, session, ...rest } = props; + + const showWhenUtilResult = validateUtil( + { + options: rest, + session, + }, + { + i18, + splitz: { abExperiments }, + }, + ); + + if (showWhenUtilResult === TAGS_API_NOT_RESOLVED_YET) { + return loader || ; + } else if (showWhenUtilResult) { + return children; + } + return null; +}; +``` + ### 3. Migration Example #### Original Custom Implimentation Code From cebac642b6000358d4b2b00b95200b88a6371774 Mon Sep 17 00:00:00 2001 From: saurabhdaware Date: Thu, 19 Sep 2024 12:42:56 +0530 Subject: [PATCH 7/8] feat: update aicodemod to use anurag's server --- packages/blade/codemods/aicodemod/bin.mjs | 47 ++++++++----------- .../codemods/aicodemod/getChatCompletion.mjs | 44 +++++++---------- .../presets/dashboard/table-pattern-1.md | 10 ++-- 3 files changed, 43 insertions(+), 58 deletions(-) diff --git a/packages/blade/codemods/aicodemod/bin.mjs b/packages/blade/codemods/aicodemod/bin.mjs index e4efe9b0f7b..06d44e1cd22 100755 --- a/packages/blade/codemods/aicodemod/bin.mjs +++ b/packages/blade/codemods/aicodemod/bin.mjs @@ -1,7 +1,6 @@ #!/usr/bin/env node import fs from 'fs'; import path from 'path'; -import { migrated, original } from './sampleMigration.mjs'; const getFlagValue = (flag) => { const flagIndex = process.argv.indexOf(flag); @@ -13,36 +12,30 @@ const getFlagValue = (flag) => { }; // This is for passing ngrok instance URL -const baseUrl = getFlagValue('--base-url') ?? 'https://blade-ds.loca.lt'; -const preset = getFlagValue('--code-knowledge') ?? 'presets/dashboard/table-pattern-1'; +const BASE_URL = 'https://blade-chat.dev.razorpay.in'; +const SERVER_ENDPOINT = '/chat'; -const SERVER_ENDPOINT = '/chat/completions'; +const preset = getFlagValue('--code-knowledge') ?? 'presets/dashboard/table-pattern-1'; const getChatCompletionFromServer = async (messages) => { - const url = baseUrl + SERVER_ENDPOINT; + const url = BASE_URL + SERVER_ENDPOINT; // Define the fetch options const options = { method: 'POST', headers: { 'Content-Type': 'application/json', + 'rzpctx-dev-serve-user': 'base', }, - body: JSON.stringify({ messages }), + body: JSON.stringify({ input: messages }), }; try { // Make the fetch call const response = await fetch(url, options); - return response.json(); + return response.text(); } catch (err) { - if (err.cause.code === 'ECONNREFUSED') { - console.log( - ">> Looks like the Blade OpenAI POC Server is not accessible on network. Tag @Saurabh on slack in #design-system channel so he can start it. P.S. This is just until POC since we don't have a dedicated server yet 🙈", - ); - process.exit(1); - } else { - throw err; - } + throw err; } }; @@ -68,7 +61,7 @@ const usage = fs.readFileSync(usageFilePath, 'utf-8'); const ultimatePrompt = ` I want you to migrate our old codebase code from custom implementation, to use our design-system component called Blade. -You fill find code snippets of these things below- +You will find code snippets of these things below- 1. Blade's Table Documentation: Documentation of our design-system table 2. Custom Component Definitions: Custom component implementations to help figure out what some of the internally used components do @@ -79,11 +72,11 @@ Return Format: - Refer to Blade's Table documentation and Custom Component Definitons and Migration Example and return the migrated code snippet of usage using Blade - Return raw migrated code without additional text - Don't change unrelated code and imports +- If something existed in previous code, but does not map to anything in new code. Add a TODO comment describing what needs to be done - Remove unused variables, functions, and imports - Make sure Blade's Header and Footer are also used wherever needed. - Ignore the code related to mobile responsiveness as Blade components are responsive by default - ## 1. Blade's Table Documentation ${bladeKnowledge} @@ -117,21 +110,19 @@ const messages = [ }, ]; -const data = await getChatCompletionFromServer(messages); +const answer = await getChatCompletionFromServer(messages); -if (data.answer) { +if (answer) { console.log('USAGE STATS', { - inputLength: data.inputLength, - openAIUsage: data.usage, - v: 9, + inputLength: JSON.stringify(messages).length, }); - const out = data.answer.content; - const cleanOut = out.startsWith('```jsx') - ? out.slice(`\`\`\`jsx`.length + 1, -('```'.length + 1)) - : out; + const cleanOut = answer.startsWith('```jsx') + ? answer.slice(`\`\`\`jsx`.length + 1, -('```'.length + 1)) + : answer; fs.writeFileSync(usageFilePath, cleanOut); } else { - console.log('status=', data.status); - console.error(data.error); + console.log( + "Something's not right. The server didn't respond correctly. Hopefully there are more helpful logs above ^^", + ); } diff --git a/packages/blade/codemods/aicodemod/getChatCompletion.mjs b/packages/blade/codemods/aicodemod/getChatCompletion.mjs index 8f5af3058fd..db01886509d 100644 --- a/packages/blade/codemods/aicodemod/getChatCompletion.mjs +++ b/packages/blade/codemods/aicodemod/getChatCompletion.mjs @@ -1,9 +1,3 @@ -const aiConfig = { - temperature: 0.7, - top_p: 0.95, - max_tokens: 3000, -}; - const getChatCompletion = async (messages) => { if (process.env.TEST_MODE === 'true') { console.log('messages:'); @@ -20,16 +14,9 @@ const getChatCompletion = async (messages) => { const headers = { 'Content-Type': 'application/json', - 'api-key': process.env.OPENAI_API_KEY, - }; - - // Payload for the request - const payload = { - messages, - ...aiConfig, }; - const GPT4V_ENDPOINT = `${process.env.OPENAI_BASE_URL}/chat/completions?api-version=2024-02-15-preview`; + const GPT4V_ENDPOINT = `https://blade-chat.dev.razorpay.in/chat`; // Send request let data; @@ -37,7 +24,9 @@ const getChatCompletion = async (messages) => { data = await fetch(GPT4V_ENDPOINT, { method: 'POST', headers: headers, - body: JSON.stringify(payload), + body: JSON.stringify({ + input: messages, + }), }).then((response) => { if (!response.ok) { console.error(`HTTP error! Status: ${response.status}`); @@ -59,21 +48,22 @@ const getChatCompletion = async (messages) => { }; } - console.log('>> USAGE', data.model, data.usage); + // console.log('>> USAGE', data.model, data.usage); - if (data.status === false) { - return { - answer: undefined, - usage: undefined, - inputLength: JSON.stringify(messages).length, - error: data.error, - status: data.status, - }; - } + // if (data.status === false) { + // return { + // answer: undefined, + // usage: undefined, + // inputLength: JSON.stringify(messages).length, + // error: data.error, + // status: data.status, + // }; + // } return { - answer: data.choices[0].message, - usage: data.usage, + answer: { + content: data, + }, inputLength: JSON.stringify(messages).length, }; }; diff --git a/packages/blade/codemods/aicodemod/knowledge/presets/dashboard/table-pattern-1.md b/packages/blade/codemods/aicodemod/knowledge/presets/dashboard/table-pattern-1.md index 692b029d6a9..0c696bc9d04 100644 --- a/packages/blade/codemods/aicodemod/knowledge/presets/dashboard/table-pattern-1.md +++ b/packages/blade/codemods/aicodemod/knowledge/presets/dashboard/table-pattern-1.md @@ -303,9 +303,13 @@ export default (props) => { - {tableData.map((invoice) => ( - - ))} + {tableData.length <= 0 ? ( + + ) : ( + tableData.map((invoice) => ( + + )) + )} )} From c717c68b5a51a0aff81cba0d8f10dfb565661b9b Mon Sep 17 00:00:00 2001 From: saurabhdaware Date: Thu, 19 Sep 2024 12:45:09 +0530 Subject: [PATCH 8/8] refactor: remove unused code --- .../codemods/aicodemod/getChatCompletion.mjs | 71 ------- .../codemods/aicodemod/sampleMigration.mjs | 191 ------------------ packages/blade/codemods/aicodemod/server.mjs | 39 ---- 3 files changed, 301 deletions(-) delete mode 100644 packages/blade/codemods/aicodemod/getChatCompletion.mjs delete mode 100644 packages/blade/codemods/aicodemod/sampleMigration.mjs delete mode 100644 packages/blade/codemods/aicodemod/server.mjs diff --git a/packages/blade/codemods/aicodemod/getChatCompletion.mjs b/packages/blade/codemods/aicodemod/getChatCompletion.mjs deleted file mode 100644 index db01886509d..00000000000 --- a/packages/blade/codemods/aicodemod/getChatCompletion.mjs +++ /dev/null @@ -1,71 +0,0 @@ -const getChatCompletion = async (messages) => { - if (process.env.TEST_MODE === 'true') { - console.log('messages:'); - console.dir(messages, { depth: Infinity }); - return { - answer: { - content: - 'The server is running in TEST_MODE environment. Its not supposed to. Blame @Saurabh for this on slack #design-system channel.', - }, - usage: { total_tokens: 0 }, - inputLength: JSON.stringify(messages).length, - }; - } - - const headers = { - 'Content-Type': 'application/json', - }; - - const GPT4V_ENDPOINT = `https://blade-chat.dev.razorpay.in/chat`; - - // Send request - let data; - try { - data = await fetch(GPT4V_ENDPOINT, { - method: 'POST', - headers: headers, - body: JSON.stringify({ - input: messages, - }), - }).then((response) => { - if (!response.ok) { - console.error(`HTTP error! Status: ${response.status}`); - return { - success: false, - status: response.status, - error: 'HTTP error!', - }; - } - - return response.json(); - }); - } catch (error) { - console.error(`Failed to make the request. Error: ${error}`); - return { - success: false, - status: 500, - error, - }; - } - - // console.log('>> USAGE', data.model, data.usage); - - // if (data.status === false) { - // return { - // answer: undefined, - // usage: undefined, - // inputLength: JSON.stringify(messages).length, - // error: data.error, - // status: data.status, - // }; - // } - - return { - answer: { - content: data, - }, - inputLength: JSON.stringify(messages).length, - }; -}; - -export { getChatCompletion }; diff --git a/packages/blade/codemods/aicodemod/sampleMigration.mjs b/packages/blade/codemods/aicodemod/sampleMigration.mjs deleted file mode 100644 index 5e7e99faae8..00000000000 --- a/packages/blade/codemods/aicodemod/sampleMigration.mjs +++ /dev/null @@ -1,191 +0,0 @@ -export const original = `import Text from '@razorpay/blade-old/src/atoms/Text'; -import View from '@razorpay/blade-old/src/atoms/View'; -import Flex from '@razorpay/blade-old/src/atoms/Flex'; -import Icon from '@razorpay/blade-old/src/atoms/Icon'; - -import TableBody from 'common/ui/TableBody'; -import MobileListView from 'common/ui/MobileListView'; -import { amount, createdAt } from 'common/ui/item/pair'; - -import EntityItemRow from 'merchant/containers/EntityItemRow'; -import { isMobileDevice } from 'merchant/components/Home/data'; -import { - customer, - invoiceId, - invoiveStatus, - paymentLink, - paymentLinkId, - receiptNumber, - referenceId, -} from 'merchant/views/Invoices/Invoices/item'; -import { shareURL } from 'merchant/views/Invoices/Invoices/helpers'; - -const PaymentLinkMobileTableListView = (items) => { - const { item, onShareLinkSuccess = () => {} } = items; - - return ( - - - {paymentLinkId.value(item)} - - {item?.customer_details && customer.value(item)} - - {item?.short_url && ( - { - onShareLinkSuccess(); - shareURL(item.short_url, item?.id); - }} - > - - -
- -
- Share payment link -
-
-
- )} - - - {amount.value(item)} - {invoiveStatus.value(item)} -
- ); -}; - -const InvoiceListItem = (props) => { - const { invoice, columns } = props; - - return ( - - {columns.map(({ title, value }) => ( - {value(invoice)} - ))} - - ); -}; - -export default (props) => { - const { - type, - invoices, - isLoading, - onCopy = () => {}, - EmptyList, - isPaymentlinksV2Enabled, - } = props; - - const isPaymentLinksType = type === 'link'; - const label = isPaymentLinksType ? paymentLinkId : invoiceId; - const listStyle = isPaymentLinksType ? 'Payment-link-list' : 'Invoice-list'; - const receipt = isPaymentlinksV2Enabled ? referenceId : receiptNumber; - - const columns = [label, createdAt, amount, receipt, customer, paymentLink(onCopy), invoiveStatus]; - - return isMobileDevice() && isPaymentLinksType ? ( - - ) : ( -
- - - - {columns.map(({ title }) => ( - - ))} - - - - {invoices.map((invoice) => ( - - ))} - -
{title}
-
- ); -}; -`; - -export const migrated = ` -import { - Table, - TableHeader, - TableHeaderRow, - TableHeaderCell, - TableBody, - TableRow, - TableCell, -} from '@razorpay/blade/components'; - -import { amount, createdAt } from 'common/ui/item/pair'; - -import { - customer, - invoiceId, - invoiveStatus, - paymentLink, - paymentLinkId, - receiptNumber, - referenceId, -} from 'merchant/views/Invoices/Invoices/item'; -import { shareURL } from 'merchant/views/Invoices/Invoices/helpers'; - -const InvoiceListItem = (props) => { - const { invoice, columns } = props; - - return ( - - {columns.map(({ title, value }) => ( - {value(invoice)} - ))} - - ); -}; - -export default (props) => { - const { - type, - invoices, - isLoading, - onCopy = () => {}, - EmptyList, - isPaymentlinksV2Enabled, - } = props; - - const isPaymentLinksType = type === 'link'; - const label = isPaymentLinksType ? paymentLinkId : invoiceId; - const receipt = isPaymentlinksV2Enabled ? referenceId : receiptNumber; - - const columns = [label, createdAt, amount, receipt, customer, paymentLink(onCopy), invoiveStatus]; - - return ( - - {(tableData) => ( - <> - - - {columns.map(({ title }) => ( - {title} - ))} - - - - {tableData.map((invoice) => ( - - ))} - - - )} -
- ); -}; -`; diff --git a/packages/blade/codemods/aicodemod/server.mjs b/packages/blade/codemods/aicodemod/server.mjs deleted file mode 100644 index fd4e2e69d18..00000000000 --- a/packages/blade/codemods/aicodemod/server.mjs +++ /dev/null @@ -1,39 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import dotenv from 'dotenv'; -import Fastify from 'fastify'; -import { getChatCompletion } from './getChatCompletion.mjs'; - -const __dirname = new URL('.', import.meta.url).pathname; -const envFilePath = path.resolve(__dirname, '../../.env.local'); - -if (!fs.existsSync(envFilePath)) { - throw new Error('.env.local does not exist in blade package root'); -} - -dotenv.config({ path: envFilePath }); - -const fastify = Fastify({ logger: true }); - -// Declare a route -fastify.post('/chat/completions', async (request, reply) => { - // Access the body of the request - const body = request.body; - - const data = await getChatCompletion(body.messages); - - // Send a response - return data; -}); - -// Run the server -const start = async () => { - try { - await fastify.listen({ port: 3000 }); - fastify.log.info(`Server is running at http://localhost:3000`); - } catch (err) { - fastify.log.error(err); - process.exit(1); - } -}; -start();