diff --git a/examples/vitepress/apexdocs.config.ts b/examples/vitepress/apexdocs.config.ts index 4a76b64f..15db7a60 100644 --- a/examples/vitepress/apexdocs.config.ts +++ b/examples/vitepress/apexdocs.config.ts @@ -33,6 +33,7 @@ export default { }), markdown: defineMarkdownConfig({ sourceDir: 'force-app', + includeMetadata: false, scope: ['global', 'public', 'protected', 'private', 'namespaceaccessible'], sortAlphabetically: true, namespace: 'apexdocs', @@ -50,36 +51,21 @@ export default { }, excludeTags: ['internal'], transformDocs: async (docs) => { + const apexOnlyDocs = docs.filter((doc) => doc.type !== 'customobject'); + const objectOnlyDocs = docs.filter((doc) => doc.type === 'customobject'); + // Update sidebar const sidebar = [ { text: 'API Reference', - items: [ - { - text: 'Grouped By Type', - items: [ - { - text: 'Classes', - items: docs.filter((doc) => doc.source.type === 'class').map(toSidebarLink), - }, - { - text: 'Interfaces', - items: docs.filter((doc) => doc.source.type === 'interface').map(toSidebarLink), - }, - { - text: 'Enums', - items: docs.filter((doc) => doc.source.type === 'enum').map(toSidebarLink), - }, - ], - }, - { - text: 'Grouped by Group', - items: Array.from(extractGroups(docs)).map(([groupName, groupDocs]) => ({ - text: groupName, - items: groupDocs.map(toSidebarLink), - })), - }, - ], + items: Array.from(extractGroups(apexOnlyDocs)).map(([groupName, groupDocs]) => ({ + text: groupName, + items: groupDocs.map(toSidebarLink), + })), + }, + { + text: 'Object Reference', + items: objectOnlyDocs.map(toSidebarLink), }, ]; await writeFileAsync('./docs/.vitepress/sidebar.json', JSON.stringify(sidebar, null, 2)); diff --git a/examples/vitepress/docs/.vitepress/sidebar.json b/examples/vitepress/docs/.vitepress/sidebar.json index a4e2b837..dd5afb72 100644 --- a/examples/vitepress/docs/.vitepress/sidebar.json +++ b/examples/vitepress/docs/.vitepress/sidebar.json @@ -3,117 +3,89 @@ "text": "API Reference", "items": [ { - "text": "Grouped By Type", + "text": "Miscellaneous", "items": [ { - "text": "Classes", - "items": [ - { - "text": "BaseClass", - "link": "miscellaneous/BaseClass.md" - }, - { - "text": "MultiInheritanceClass", - "link": "miscellaneous/MultiInheritanceClass.md" - }, - { - "text": "SampleClass", - "link": "samplegroup/SampleClass.md" - }, - { - "text": "SampleException", - "link": "miscellaneous/SampleException.md" - }, - { - "text": "Url", - "link": "miscellaneous/Url.md" - } - ] + "text": "BaseClass", + "link": "miscellaneous/BaseClass.md" }, { - "text": "Interfaces", - "items": [ - { - "text": "ParentInterface", - "link": "miscellaneous/ParentInterface.md" - }, - { - "text": "SampleInterface", - "link": "miscellaneous/SampleInterface.md" - } - ] + "text": "MultiInheritanceClass", + "link": "miscellaneous/MultiInheritanceClass.md" }, { - "text": "Enums", - "items": [ - { - "text": "ReferencedEnum", - "link": "miscellaneous/ReferencedEnum.md" - }, - { - "text": "SampleEnum", - "link": "sample-enums/SampleEnum.md" - } - ] + "text": "ParentInterface", + "link": "miscellaneous/ParentInterface.md" + }, + { + "text": "ReferencedEnum", + "link": "miscellaneous/ReferencedEnum.md" + }, + { + "text": "SampleException", + "link": "miscellaneous/SampleException.md" + }, + { + "text": "SampleInterface", + "link": "miscellaneous/SampleInterface.md" + }, + { + "text": "Url", + "link": "miscellaneous/Url.md" } ] }, { - "text": "Grouped by Group", + "text": "SampleGroup", "items": [ { - "text": "Miscellaneous", - "items": [ - { - "text": "BaseClass", - "link": "miscellaneous/BaseClass.md" - }, - { - "text": "MultiInheritanceClass", - "link": "miscellaneous/MultiInheritanceClass.md" - }, - { - "text": "ParentInterface", - "link": "miscellaneous/ParentInterface.md" - }, - { - "text": "ReferencedEnum", - "link": "miscellaneous/ReferencedEnum.md" - }, - { - "text": "SampleException", - "link": "miscellaneous/SampleException.md" - }, - { - "text": "SampleInterface", - "link": "miscellaneous/SampleInterface.md" - }, - { - "text": "Url", - "link": "miscellaneous/Url.md" - } - ] - }, - { - "text": "SampleGroup", - "items": [ - { - "text": "SampleClass", - "link": "samplegroup/SampleClass.md" - } - ] - }, + "text": "SampleClass", + "link": "samplegroup/SampleClass.md" + } + ] + }, + { + "text": "Sample Enums", + "items": [ { - "text": "Sample Enums", - "items": [ - { - "text": "SampleEnum", - "link": "sample-enums/SampleEnum.md" - } - ] + "text": "SampleEnum", + "link": "sample-enums/SampleEnum.md" } ] } ] + }, + { + "text": "Object Reference", + "items": [ + { + "text": "Event__c", + "link": "custom-objects/Event__c.md" + }, + { + "text": "Price_Component__c", + "link": "custom-objects/Price_Component__c.md" + }, + { + "text": "Product__c", + "link": "custom-objects/Product__c.md" + }, + { + "text": "Product_Price_Component__c", + "link": "custom-objects/Product_Price_Component__c.md" + }, + { + "text": "Sales_Order__c", + "link": "custom-objects/Sales_Order__c.md" + }, + { + "text": "Sales_Order_Line__c", + "link": "custom-objects/Sales_Order_Line__c.md" + }, + { + "text": "Speaker__c", + "link": "custom-objects/Speaker__c.md" + } + ] } ] \ No newline at end of file diff --git a/examples/vitepress/docs/custom-objects/Event__c.md b/examples/vitepress/docs/custom-objects/Event__c.md new file mode 100644 index 00000000..892379a4 --- /dev/null +++ b/examples/vitepress/docs/custom-objects/Event__c.md @@ -0,0 +1,65 @@ +--- +title: Event__c +--- + +# Event + +Represents an event that people can register for. + +## API Name +`apexdocs__Event__c` + +## Fields +### Description + +**API Name** + +`apexdocs__Description__c` + +**Type** + +*LongTextArea* + +--- +### End Date + +**API Name** + +`apexdocs__End_Date__c` + +**Type** + +*Date* + +--- +### Location + +**API Name** + +`apexdocs__Location__c` + +**Type** + +*Location* + +--- +### Start Date + +**API Name** + +`apexdocs__Start_Date__c` + +**Type** + +*Date* + +--- +### Tag Line + +**API Name** + +`apexdocs__Tag_Line__c` + +**Type** + +*Text* \ No newline at end of file diff --git a/examples/vitepress/docs/custom-objects/Price_Component__c.md b/examples/vitepress/docs/custom-objects/Price_Component__c.md new file mode 100644 index 00000000..053ccb3c --- /dev/null +++ b/examples/vitepress/docs/custom-objects/Price_Component__c.md @@ -0,0 +1,69 @@ +--- +title: Price_Component__c +--- + +# Price Component + +## API Name +`apexdocs__Price_Component__c` + +## Fields +### Description + +**API Name** + +`apexdocs__Description__c` + +**Type** + +*Text* + +--- +### Expression + +The Expression that determines if this price should take effect or not. + +**API Name** + +`apexdocs__Expression__c` + +**Type** + +*LongTextArea* + +--- +### Percent + +Use this field to calculate the price based on the list price's percentage instead of providing a flat price. + +**API Name** + +`apexdocs__Percent__c` + +**Type** + +*Percent* + +--- +### Price + +Use this when the Price Component represents a Flat Price. To represent a Percentage use the Percent field. + +**API Name** + +`apexdocs__Price__c` + +**Type** + +*Currency* + +--- +### Type + +**API Name** + +`apexdocs__Type__c` + +**Type** + +*Picklist* \ No newline at end of file diff --git a/examples/vitepress/docs/custom-objects/Product_Price_Component__c.md b/examples/vitepress/docs/custom-objects/Product_Price_Component__c.md new file mode 100644 index 00000000..cfe9dffa --- /dev/null +++ b/examples/vitepress/docs/custom-objects/Product_Price_Component__c.md @@ -0,0 +1,30 @@ +--- +title: Product_Price_Component__c +--- + +# Product Price Component + +## API Name +`apexdocs__Product_Price_Component__c` + +## Fields +### Price Component + +**API Name** + +`apexdocs__Price_Component__c` + +**Type** + +*MasterDetail* + +--- +### Product + +**API Name** + +`apexdocs__Product__c` + +**Type** + +*MasterDetail* \ No newline at end of file diff --git a/examples/vitepress/docs/custom-objects/Product__c.md b/examples/vitepress/docs/custom-objects/Product__c.md new file mode 100644 index 00000000..ba3e70d6 --- /dev/null +++ b/examples/vitepress/docs/custom-objects/Product__c.md @@ -0,0 +1,43 @@ +--- +title: Product__c +--- + +# Product (Custom) + +Product that is sold or available for sale. + +## API Name +`apexdocs__Product__c` + +## Fields +### Description + +**API Name** + +`apexdocs__Description__c` + +**Type** + +*Text* + +--- +### Event + +**API Name** + +`apexdocs__Event__c` + +**Type** + +*Lookup* + +--- +### Features + +**API Name** + +`apexdocs__Features__c` + +**Type** + +*LongTextArea* \ No newline at end of file diff --git a/examples/vitepress/docs/custom-objects/Sales_Order_Line__c.md b/examples/vitepress/docs/custom-objects/Sales_Order_Line__c.md new file mode 100644 index 00000000..041e94ce --- /dev/null +++ b/examples/vitepress/docs/custom-objects/Sales_Order_Line__c.md @@ -0,0 +1,65 @@ +--- +title: Sales_Order_Line__c +--- + +# Sales Order Line + +Represents a line item on a sales order. + +## API Name +`apexdocs__Sales_Order_Line__c` + +## Fields +### Amount + +**API Name** + +`apexdocs__Amount__c` + +**Type** + +*Currency* + +--- +### Product + +**API Name** + +`apexdocs__Product__c` + +**Type** + +*Lookup* + +--- +### Sales Order + +**API Name** + +`apexdocs__Sales_Order__c` + +**Type** + +*MasterDetail* + +--- +### Source Price Component + +**API Name** + +`apexdocs__Source_Price_Component__c` + +**Type** + +*Lookup* + +--- +### Type + +**API Name** + +`apexdocs__Type__c` + +**Type** + +*Picklist* \ No newline at end of file diff --git a/examples/vitepress/docs/custom-objects/Sales_Order__c.md b/examples/vitepress/docs/custom-objects/Sales_Order__c.md new file mode 100644 index 00000000..220efa21 --- /dev/null +++ b/examples/vitepress/docs/custom-objects/Sales_Order__c.md @@ -0,0 +1,10 @@ +--- +title: Sales_Order__c +--- + +# Sales Order + +Custom object for tracking sales orders. + +## API Name +`apexdocs__Sales_Order__c` \ No newline at end of file diff --git a/examples/vitepress/docs/custom-objects/Speaker__c.md b/examples/vitepress/docs/custom-objects/Speaker__c.md new file mode 100644 index 00000000..093efbf4 --- /dev/null +++ b/examples/vitepress/docs/custom-objects/Speaker__c.md @@ -0,0 +1,43 @@ +--- +title: Speaker__c +--- + +# Speaker + +Represents a speaker at an event. + +## API Name +`apexdocs__Speaker__c` + +## Fields +### About + +**API Name** + +`apexdocs__About__c` + +**Type** + +*LongTextArea* + +--- +### Event + +**API Name** + +`apexdocs__Event__c` + +**Type** + +*MasterDetail* + +--- +### Person + +**API Name** + +`apexdocs__Person__c` + +**Type** + +*MasterDetail* \ No newline at end of file diff --git a/examples/vitepress/docs/index.md b/examples/vitepress/docs/index.md index cdb614cd..e8629caa 100644 --- a/examples/vitepress/docs/index.md +++ b/examples/vitepress/docs/index.md @@ -15,7 +15,33 @@ hero: link: /api-examples --- -# Apex Reference Guide +# Reference Guide + +## Custom Objects + +### [Event__c](custom-objects/Event__c) + +Represents an event that people can register for. + +### [Price_Component__c](custom-objects/Price_Component__c) + +### [Product__c](custom-objects/Product__c) + +Product that is sold or available for sale. + +### [Product_Price_Component__c](custom-objects/Product_Price_Component__c) + +### [Sales_Order__c](custom-objects/Sales_Order__c) + +Custom object for tracking sales orders. + +### [Sales_Order_Line__c](custom-objects/Sales_Order_Line__c) + +Represents a line item on a sales order. + +### [Speaker__c](custom-objects/Speaker__c) + +Represents a speaker at an event. ## Miscellaneous diff --git a/examples/vitepress/force-app/main/default/classes/BaseClass.cls-meta.xml b/examples/vitepress/force-app/main/default/classes/BaseClass.cls-meta.xml new file mode 100644 index 00000000..998805a8 --- /dev/null +++ b/examples/vitepress/force-app/main/default/classes/BaseClass.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/examples/vitepress/force-app/main/default/classes/MultiInheritanceClass.cls-meta.xml b/examples/vitepress/force-app/main/default/classes/MultiInheritanceClass.cls-meta.xml new file mode 100644 index 00000000..998805a8 --- /dev/null +++ b/examples/vitepress/force-app/main/default/classes/MultiInheritanceClass.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/examples/vitepress/force-app/main/default/classes/ParentInterface.cls-meta.xml b/examples/vitepress/force-app/main/default/classes/ParentInterface.cls-meta.xml new file mode 100644 index 00000000..998805a8 --- /dev/null +++ b/examples/vitepress/force-app/main/default/classes/ParentInterface.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/examples/vitepress/force-app/main/default/classes/ReferencedEnum.cls-meta.xml b/examples/vitepress/force-app/main/default/classes/ReferencedEnum.cls-meta.xml new file mode 100644 index 00000000..998805a8 --- /dev/null +++ b/examples/vitepress/force-app/main/default/classes/ReferencedEnum.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/examples/vitepress/force-app/main/default/classes/Url.cls-meta.xml b/examples/vitepress/force-app/main/default/classes/Url.cls-meta.xml new file mode 100644 index 00000000..998805a8 --- /dev/null +++ b/examples/vitepress/force-app/main/default/classes/Url.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/examples/vitepress/force-app/main/default/classes/feature-a/SampleClass.cls-meta.xml b/examples/vitepress/force-app/main/default/classes/feature-a/SampleClass.cls-meta.xml new file mode 100644 index 00000000..998805a8 --- /dev/null +++ b/examples/vitepress/force-app/main/default/classes/feature-a/SampleClass.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/examples/vitepress/force-app/main/default/classes/feature-a/SampleEnum.cls-meta.xml b/examples/vitepress/force-app/main/default/classes/feature-a/SampleEnum.cls-meta.xml new file mode 100644 index 00000000..998805a8 --- /dev/null +++ b/examples/vitepress/force-app/main/default/classes/feature-a/SampleEnum.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/examples/vitepress/force-app/main/default/classes/feature-a/SampleException.cls-meta.xml b/examples/vitepress/force-app/main/default/classes/feature-a/SampleException.cls-meta.xml new file mode 100644 index 00000000..998805a8 --- /dev/null +++ b/examples/vitepress/force-app/main/default/classes/feature-a/SampleException.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/examples/vitepress/force-app/main/default/classes/feature-a/SampleInterface.cls-meta.xml b/examples/vitepress/force-app/main/default/classes/feature-a/SampleInterface.cls-meta.xml new file mode 100644 index 00000000..998805a8 --- /dev/null +++ b/examples/vitepress/force-app/main/default/classes/feature-a/SampleInterface.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/examples/vitepress/force-app/main/default/objects/Contact/fields/PhotoUrl__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Contact/fields/PhotoUrl__c.field-meta.xml new file mode 100644 index 00000000..a9117781 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Contact/fields/PhotoUrl__c.field-meta.xml @@ -0,0 +1,9 @@ + + + PhotoUrl__c + false + + false + false + Url + diff --git a/examples/vitepress/force-app/main/default/objects/Event__c/Event__c.object-meta.xml b/examples/vitepress/force-app/main/default/objects/Event__c/Event__c.object-meta.xml new file mode 100644 index 00000000..d30dde5c --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Event__c/Event__c.object-meta.xml @@ -0,0 +1,167 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + Private + + + + Text + + Events + Represents an event that people can register for. + + ReadWrite + Vowel + Public + diff --git a/examples/vitepress/force-app/main/default/objects/Event__c/fields/Description__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Event__c/fields/Description__c.field-meta.xml new file mode 100644 index 00000000..c1b682a4 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Event__c/fields/Description__c.field-meta.xml @@ -0,0 +1,10 @@ + + + Description__c + false + + 32768 + false + LongTextArea + 10 + diff --git a/examples/vitepress/force-app/main/default/objects/Event__c/fields/End_Date__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Event__c/fields/End_Date__c.field-meta.xml new file mode 100644 index 00000000..422a0003 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Event__c/fields/End_Date__c.field-meta.xml @@ -0,0 +1,9 @@ + + + End_Date__c + false + + true + false + Date + diff --git a/examples/vitepress/force-app/main/default/objects/Event__c/fields/Location__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Event__c/fields/Location__c.field-meta.xml new file mode 100644 index 00000000..b8f32121 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Event__c/fields/Location__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Location__c + false + false + + true + 3 + false + Location + diff --git a/examples/vitepress/force-app/main/default/objects/Event__c/fields/Start_Date__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Event__c/fields/Start_Date__c.field-meta.xml new file mode 100644 index 00000000..81fb3f6d --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Event__c/fields/Start_Date__c.field-meta.xml @@ -0,0 +1,9 @@ + + + Start_Date__c + false + + true + false + Date + diff --git a/examples/vitepress/force-app/main/default/objects/Event__c/fields/Tag_Line__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Event__c/fields/Tag_Line__c.field-meta.xml new file mode 100644 index 00000000..652ee2e0 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Event__c/fields/Tag_Line__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Tag_Line__c + false + + 255 + false + false + Text + false + diff --git a/examples/vitepress/force-app/main/default/objects/Price_Component__c/Price_Component__c.object-meta.xml b/examples/vitepress/force-app/main/default/objects/Price_Component__c/Price_Component__c.object-meta.xml new file mode 100644 index 00000000..ae72fd0c --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Price_Component__c/Price_Component__c.object-meta.xml @@ -0,0 +1,169 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Action override created by Lightning App Builder during activation. + Price_Component_Record_Page + Large + false + Flexipage + + + View + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + Private + + + PC-{0000} + + AutoNumber + + Price Components + + ReadWrite + Public + diff --git a/examples/vitepress/force-app/main/default/objects/Price_Component__c/fields/Description__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Price_Component__c/fields/Description__c.field-meta.xml new file mode 100644 index 00000000..69050ca6 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Price_Component__c/fields/Description__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Description__c + false + + 255 + false + false + Text + false + diff --git a/examples/vitepress/force-app/main/default/objects/Price_Component__c/fields/Expression__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Price_Component__c/fields/Expression__c.field-meta.xml new file mode 100644 index 00000000..c0bf4e45 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Price_Component__c/fields/Expression__c.field-meta.xml @@ -0,0 +1,12 @@ + + + Expression__c + The Expression that determines if this price should take effect or not. + false + The Expression that determines if this price should take effect or not. + + 131072 + false + LongTextArea + 20 + diff --git a/examples/vitepress/force-app/main/default/objects/Price_Component__c/fields/Percent__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Price_Component__c/fields/Percent__c.field-meta.xml new file mode 100644 index 00000000..9c303bc4 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Price_Component__c/fields/Percent__c.field-meta.xml @@ -0,0 +1,13 @@ + + + Percent__c + Use this field to calculate the price based on the list price's percentage instead of providing a flat price. + false + Use this field to calculate the price based on the list price's percentage instead of providing a flat price. + + 18 + false + 0 + false + Percent + diff --git a/examples/vitepress/force-app/main/default/objects/Price_Component__c/fields/Price__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Price_Component__c/fields/Price__c.field-meta.xml new file mode 100644 index 00000000..84136dec --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Price_Component__c/fields/Price__c.field-meta.xml @@ -0,0 +1,13 @@ + + + Price__c + Use this when the Price Component represents a Flat Price. To represent a Percentage use the Percent field. + false + Use this when the Price Component represents a Flat Price. To represent a Percentage use the Percent field. + + 18 + false + 2 + false + Currency + diff --git a/examples/vitepress/force-app/main/default/objects/Price_Component__c/fields/Type__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Price_Component__c/fields/Type__c.field-meta.xml new file mode 100644 index 00000000..c430b305 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Price_Component__c/fields/Type__c.field-meta.xml @@ -0,0 +1,30 @@ + + + Type__c + false + + true + false + Picklist + + true + + false + + List Price + false + + + + Surcharge + false + + + + Discount + false + + + + + diff --git a/examples/vitepress/force-app/main/default/objects/Product_Price_Component__c/Product_Price_Component__c.object-meta.xml b/examples/vitepress/force-app/main/default/objects/Product_Price_Component__c/Product_Price_Component__c.object-meta.xml new file mode 100644 index 00000000..8a9a6348 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Product_Price_Component__c/Product_Price_Component__c.object-meta.xml @@ -0,0 +1,166 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + ControlledByParent + + + PPC-{0000} + + AutoNumber + + Product Price Components + + ControlledByParent + Public + diff --git a/examples/vitepress/force-app/main/default/objects/Product_Price_Component__c/fields/Price_Component__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Product_Price_Component__c/fields/Price_Component__c.field-meta.xml new file mode 100644 index 00000000..f152ecb6 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Product_Price_Component__c/fields/Price_Component__c.field-meta.xml @@ -0,0 +1,14 @@ + + + Price_Component__c + false + + Price_Component__c + Product Price Components + Product_Price_Components + 1 + false + false + MasterDetail + false + diff --git a/examples/vitepress/force-app/main/default/objects/Product_Price_Component__c/fields/Product__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Product_Price_Component__c/fields/Product__c.field-meta.xml new file mode 100644 index 00000000..16ec5b33 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Product_Price_Component__c/fields/Product__c.field-meta.xml @@ -0,0 +1,14 @@ + + + Product__c + false + + Product__c + Product Price Components + Product_Price_Components + 0 + false + false + MasterDetail + false + diff --git a/examples/vitepress/force-app/main/default/objects/Product__c/Product__c.object-meta.xml b/examples/vitepress/force-app/main/default/objects/Product__c/Product__c.object-meta.xml new file mode 100644 index 00000000..cdeb52a9 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Product__c/Product__c.object-meta.xml @@ -0,0 +1,169 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Action override created by Lightning App Builder during activation. + Product_Record_Page + Large + false + Flexipage + + + View + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + Private + + Product that is sold or available for sale. + + + Text + + Products + + ReadWrite + Public + diff --git a/examples/vitepress/force-app/main/default/objects/Product__c/fields/Description__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Product__c/fields/Description__c.field-meta.xml new file mode 100644 index 00000000..69050ca6 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Product__c/fields/Description__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Description__c + false + + 255 + false + false + Text + false + diff --git a/examples/vitepress/force-app/main/default/objects/Product__c/fields/Event__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Product__c/fields/Event__c.field-meta.xml new file mode 100644 index 00000000..82947d0b --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Product__c/fields/Event__c.field-meta.xml @@ -0,0 +1,12 @@ + + + Event__c + Restrict + false + + Event__c + Products + true + false + Lookup + diff --git a/examples/vitepress/force-app/main/default/objects/Product__c/fields/Features__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Product__c/fields/Features__c.field-meta.xml new file mode 100644 index 00000000..6b67a859 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Product__c/fields/Features__c.field-meta.xml @@ -0,0 +1,10 @@ + + + Features__c + false + + 32768 + false + LongTextArea + 10 + diff --git a/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/Sales_Order_Line__c.object-meta.xml b/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/Sales_Order_Line__c.object-meta.xml new file mode 100644 index 00000000..36e9348d --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/Sales_Order_Line__c.object-meta.xml @@ -0,0 +1,167 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + ControlledByParent + + Represents a line item on a sales order. + + SOL-{0000} + + AutoNumber + + Sales Order Lines + + ControlledByParent + Public + diff --git a/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/fields/Amount__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/fields/Amount__c.field-meta.xml new file mode 100644 index 00000000..3a464e2d --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/fields/Amount__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Amount__c + false + + 18 + true + 2 + false + Currency + diff --git a/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/fields/Product__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/fields/Product__c.field-meta.xml new file mode 100644 index 00000000..b6b5369f --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/fields/Product__c.field-meta.xml @@ -0,0 +1,13 @@ + + + Product__c + Restrict + false + + Product__c + Sales Order Lines + Sales_Order_Lines + true + false + Lookup + diff --git a/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/fields/Sales_Order__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/fields/Sales_Order__c.field-meta.xml new file mode 100644 index 00000000..c1d881c8 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/fields/Sales_Order__c.field-meta.xml @@ -0,0 +1,14 @@ + + + Sales_Order__c + false + + Sales_Order__c + Sales Order Lines + Sales_Order_Lines + 0 + false + false + MasterDetail + false + diff --git a/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/fields/Source_Price_Component__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/fields/Source_Price_Component__c.field-meta.xml new file mode 100644 index 00000000..69817d96 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/fields/Source_Price_Component__c.field-meta.xml @@ -0,0 +1,13 @@ + + + Source_Price_Component__c + SetNull + false + + Price_Component__c + Sales Order Lines + Sales_Order_Lines + false + false + Lookup + diff --git a/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/fields/Type__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/fields/Type__c.field-meta.xml new file mode 100644 index 00000000..328b5529 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Sales_Order_Line__c/fields/Type__c.field-meta.xml @@ -0,0 +1,26 @@ + + + Type__c + "Charge" + false + + true + false + Picklist + + true + + false + + Charge + false + + + + Discount + false + + + + + diff --git a/examples/vitepress/force-app/main/default/objects/Sales_Order__c/Sales_Order__c.object-meta.xml b/examples/vitepress/force-app/main/default/objects/Sales_Order__c/Sales_Order__c.object-meta.xml new file mode 100644 index 00000000..2225e4f9 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Sales_Order__c/Sales_Order__c.object-meta.xml @@ -0,0 +1,170 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Action override created by Lightning App Builder during activation. + Sales_Order_Record_Page + Large + false + Flexipage + + + View + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + Private + + Custom object for tracking sales orders. + + SO-{0000} + + AutoNumber + + Sales Orders + + ReadWrite + Public + diff --git a/examples/vitepress/force-app/main/default/objects/Speaker__c/Speaker__c.object-meta.xml b/examples/vitepress/force-app/main/default/objects/Speaker__c/Speaker__c.object-meta.xml new file mode 100644 index 00000000..6bdf2199 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Speaker__c/Speaker__c.object-meta.xml @@ -0,0 +1,167 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + ControlledByParent + + Represents a speaker at an event. + + SPEAK-{0000} + + AutoNumber + + Speakers + + ControlledByParent + Public + diff --git a/examples/vitepress/force-app/main/default/objects/Speaker__c/fields/About__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Speaker__c/fields/About__c.field-meta.xml new file mode 100644 index 00000000..2fc71d94 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Speaker__c/fields/About__c.field-meta.xml @@ -0,0 +1,10 @@ + + + About__c + false + + 32768 + false + LongTextArea + 3 + diff --git a/examples/vitepress/force-app/main/default/objects/Speaker__c/fields/Event__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Speaker__c/fields/Event__c.field-meta.xml new file mode 100644 index 00000000..cf6bfc63 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Speaker__c/fields/Event__c.field-meta.xml @@ -0,0 +1,14 @@ + + + Event__c + false + + Event__c + Speakers + Speakers + 0 + false + false + MasterDetail + false + diff --git a/examples/vitepress/force-app/main/default/objects/Speaker__c/fields/Person__c.field-meta.xml b/examples/vitepress/force-app/main/default/objects/Speaker__c/fields/Person__c.field-meta.xml new file mode 100644 index 00000000..b7ac07b1 --- /dev/null +++ b/examples/vitepress/force-app/main/default/objects/Speaker__c/fields/Person__c.field-meta.xml @@ -0,0 +1,14 @@ + + + Person__c + false + + Contact + Speakers + Speakers + 1 + false + false + MasterDetail + false + diff --git a/package-lock.json b/package-lock.json index 6c811b16..4cb39f91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,16 @@ { "name": "@cparra/apexdocs", - "version": "3.2.2", + "version": "3.3.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cparra/apexdocs", - "version": "3.2.2", + "version": "3.3.1", "license": "MIT", "dependencies": { "@cparra/apex-reflection": "2.15.0", + "@salesforce/source-deploy-retrieve": "^12.8.1", "@types/js-yaml": "^4.0.9", "@types/yargs": "^17.0.32", "chalk": "^4.1.2", @@ -1950,6 +1951,28 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsforce/jsforce-node": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@jsforce/jsforce-node/-/jsforce-node-3.5.2.tgz", + "integrity": "sha512-WZZo7HVFQsTeHRfykMJBrEK3ACr9sQnWSm3EVLasAnAUxjh+hjU4SfJ7HB1UaPRliHjLVZcSCUwF+OoJqjbfoA==", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4", + "base64url": "^3.0.1", + "csv-parse": "^5.5.2", + "csv-stringify": "^6.4.4", + "faye": "^1.4.0", + "form-data": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "multistream": "^3.1.0", + "node-fetch": "^2.6.1", + "strip-ansi": "^6.0.0", + "xml2js": "^0.6.2" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2449,12 +2472,168 @@ "win32" ] }, + "node_modules/@salesforce/core": { + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/@salesforce/core/-/core-8.6.2.tgz", + "integrity": "sha512-LFzTLnavDeWsZBB7b2iuVz0F6yeuTcJzQxCy5n+rACY2/Lbw6UJDK/bOSt4wlss6fKrkyU1FTHNlUK5ZoBEveg==", + "license": "BSD-3-Clause", + "dependencies": { + "@jsforce/jsforce-node": "^3.4.1", + "@salesforce/kit": "^3.2.2", + "@salesforce/schemas": "^1.9.0", + "@salesforce/ts-types": "^2.0.10", + "ajv": "^8.17.1", + "change-case": "^4.1.2", + "fast-levenshtein": "^3.0.0", + "faye": "^1.4.0", + "form-data": "^4.0.0", + "js2xmlparser": "^4.0.1", + "jsonwebtoken": "9.0.2", + "jszip": "3.10.1", + "pino": "^9.4.0", + "pino-abstract-transport": "^1.2.0", + "pino-pretty": "^11.2.2", + "proper-lockfile": "^4.1.2", + "semver": "^7.6.3", + "ts-retry-promise": "^0.8.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@salesforce/core/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@salesforce/core/node_modules/fast-levenshtein": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz", + "integrity": "sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==", + "license": "MIT", + "dependencies": { + "fastest-levenshtein": "^1.0.7" + } + }, + "node_modules/@salesforce/core/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/@salesforce/core/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@salesforce/kit": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@salesforce/kit/-/kit-3.2.3.tgz", + "integrity": "sha512-X8rZouLt06dxRkn+uYTwywWDS/NqZ783AyomGqgtWdUxF61EOJvu0ehtcYeutx9Ng08uuZ+s6wNvWiDsdhUcPg==", + "license": "BSD-3-Clause", + "dependencies": { + "@salesforce/ts-types": "^2.0.12" + } + }, + "node_modules/@salesforce/schemas": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@salesforce/schemas/-/schemas-1.9.0.tgz", + "integrity": "sha512-LiN37zG5ODT6z70sL1fxF7BQwtCX9JOWofSU8iliSNIM+WDEeinnoFtVqPInRSNt8I0RiJxIKCrqstsmQRBNvA==", + "license": "ISC" + }, + "node_modules/@salesforce/source-deploy-retrieve": { + "version": "12.8.1", + "resolved": "https://registry.npmjs.org/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-12.8.1.tgz", + "integrity": "sha512-1wTP6Qa9aWuToY5VMMO0Xg8ea5Vtnaf79ZYJry4BTK9y2XyzhrmwSHTvvO7UuhDb//zx2REfKT53JltCysEADA==", + "license": "BSD-3-Clause", + "dependencies": { + "@salesforce/core": "^8.6.2", + "@salesforce/kit": "^3.2.2", + "@salesforce/ts-types": "^2.0.12", + "fast-levenshtein": "^3.0.0", + "fast-xml-parser": "^4.5.0", + "got": "^11.8.6", + "graceful-fs": "^4.2.11", + "ignore": "^5.3.2", + "isbinaryfile": "^5.0.2", + "jszip": "^3.10.1", + "mime": "2.6.0", + "minimatch": "^9.0.5", + "proxy-agent": "^6.4.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@salesforce/source-deploy-retrieve/node_modules/fast-levenshtein": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz", + "integrity": "sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==", + "license": "MIT", + "dependencies": { + "fastest-levenshtein": "^1.0.7" + } + }, + "node_modules/@salesforce/source-deploy-retrieve/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@salesforce/ts-types": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@salesforce/ts-types/-/ts-types-2.0.12.tgz", + "integrity": "sha512-BIJyduJC18Kc8z+arUm5AZ9VkPRyw1KKAm+Tk+9LT99eOzhNilyfKzhZ4t+tG2lIGgnJpmytZfVDZ0e2kFul8g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -2473,6 +2652,24 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.1.18", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz", @@ -2514,6 +2711,18 @@ "@babel/types": "^7.3.0" } }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, "node_modules/@types/eslint": { "version": "8.56.10", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", @@ -2551,6 +2760,12 @@ "@types/node": "*" } }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "license": "MIT" + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", @@ -2597,6 +2812,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "20.14.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", @@ -2612,6 +2836,15 @@ "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", "dev": true }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -2861,6 +3094,18 @@ "dev": true, "license": "ISC" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", @@ -2884,6 +3129,18 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2976,6 +3233,39 @@ "node": ">=8" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" + }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/babel-plugin-istanbul": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", @@ -3020,6 +3310,44 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -3093,6 +3421,36 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3111,6 +3469,48 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3119,6 +3519,16 @@ "node": ">=6" } }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -3148,6 +3558,17 @@ } ] }, + "node_modules/capital-case": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", + "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3218,6 +3639,26 @@ "node": ">=8" } }, + "node_modules/change-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", + "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", + "license": "MIT", + "dependencies": { + "camel-case": "^4.1.2", + "capital-case": "^1.0.4", + "constant-case": "^3.0.4", + "dot-case": "^3.0.4", + "header-case": "^2.0.4", + "no-case": "^3.0.4", + "param-case": "^3.0.4", + "pascal-case": "^3.1.2", + "path-case": "^3.0.4", + "sentence-case": "^3.0.4", + "snake-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", @@ -3383,6 +3824,18 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -3416,9 +3869,20 @@ "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", @@ -3442,6 +3906,23 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/constant-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", + "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case": "^2.0.2" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/cosmiconfig": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", @@ -3519,12 +4000,53 @@ "node": ">= 8" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { + "node_modules/csprng": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/csprng/-/csprng-0.1.2.tgz", + "integrity": "sha512-D3WAbvvgUVIqSxUfdvLeGjuotsB32bvfVPd+AaaTWMtyUeC9zgCnw5xs94no89yFLVsafvY9dMZEhTwsY/ZecA==", + "license": "MIT", + "dependencies": { + "sequin": "*" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/csv-parse": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.5.6.tgz", + "integrity": "sha512-uNpm30m/AGSkLxxy7d9yRXpJQFrZzVWLFBkS+6ngPcZkw/5k3L/jjFuj7tVnEpRn+QgmiXr21nDlhCiUK4ij2A==", + "license": "MIT" + }, + "node_modules/csv-stringify": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.5.1.tgz", + "integrity": "sha512-+9lpZfwpLntpTIEpFbwQyWuW/hmI/eHuJZD1XzeZpfZTqkf1fyvBbBLXTJJMsBuuS11uTShMqPwzx4A6ffXgRQ==", + "license": "MIT" + }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { "ms": "2.1.2" }, "engines": { @@ -3536,6 +4058,33 @@ } } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dedent": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", @@ -3565,6 +4114,38 @@ "node": ">=0.10.0" } }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -3609,6 +4190,16 @@ "node": ">=6.0.0" } }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -3616,6 +4207,15 @@ "dev": true, "license": "MIT" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.795", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.795.tgz", @@ -3639,6 +4239,15 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -3711,6 +4320,27 @@ "node": ">=0.8.0" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, "node_modules/eslint": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", @@ -3965,7 +4595,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -4003,7 +4632,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "engines": { "node": ">=4.0" } @@ -4018,12 +4646,20 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", @@ -4031,6 +4667,15 @@ "dev": true, "license": "MIT" }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -4079,11 +4724,16 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/fast-copy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", + "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, "node_modules/fast-glob": { @@ -4128,10 +4778,31 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "license": "BSD-3-Clause" + }, "node_modules/fast-xml-parser": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", - "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", + "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", "funding": [ { "type": "github", @@ -4150,6 +4821,15 @@ "fxparser": "src/cli/cli.js" } }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -4159,6 +4839,35 @@ "reusify": "^1.0.4" } }, + "node_modules/faye": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/faye/-/faye-1.4.0.tgz", + "integrity": "sha512-kRrIg4be8VNYhycS2PY//hpBJSzZPr/DBbcy9VWelhZMW3KhyLkQR0HL0k0MNpmVoNFF4EdfMFkNAWjTP65g6w==", + "license": "Apache-2.0", + "dependencies": { + "asap": "*", + "csprng": "*", + "faye-websocket": ">=0.9.1", + "safe-buffer": "*", + "tough-cookie": "*", + "tunnel-agent": "*" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/fb-watchman": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", @@ -4275,12 +4984,40 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fp-ts": { "version": "2.16.8", "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.8.tgz", "integrity": "sha512-nmDtNqmMZkOxu0M5hkrS9YA15/KPkYkILb6Axg9XBAoUoYEtzg+LFmVWqZrl9FNttsW0qIUpx9RCA9INbv+Bxw==", "license": "MIT" }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4361,6 +5098,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-uri": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", + "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4", + "fs-extra": "^11.2.0" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -4445,11 +5197,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, "node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "dev": true + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" }, "node_modules/graphemer": { "version": "1.4.0", @@ -4497,12 +5274,91 @@ "node": ">= 0.4" } }, + "node_modules/header-case": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", + "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", + "license": "MIT", + "dependencies": { + "capital-case": "^1.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT" + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "license": "BSD-2-Clause" + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -4528,15 +5384,41 @@ "url": "https://github.com/sponsors/typicode" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", "engines": { "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -4593,8 +5475,26 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause" }, "node_modules/is-arrayish": { "version": "0.2.1", @@ -4712,11 +5612,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isbinaryfile": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.3.tgz", + "integrity": "sha512-VR4gNjFaDP8csJQvzInG20JvBj8MaHYLxNOMXysxRbGM7tcsHZwCjhch3FubFtZBkuDbN55i4dUukGeIrzF+6g==", + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", @@ -5987,6 +6905,15 @@ "jiti": "bin/jiti.js" } }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6003,6 +6930,21 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "license": "Apache-2.0", + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "license": "MIT" + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -6019,7 +6961,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, "license": "MIT" }, "node_modules/json-parse-even-better-errors": { @@ -6052,11 +6993,89 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, "license": "MIT", "dependencies": { "json-buffer": "3.0.1" @@ -6093,6 +7112,15 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lilconfig": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", @@ -6467,6 +7495,42 @@ "node": ">=8" } }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -6479,6 +7543,30 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/lru-cache": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz", @@ -6570,6 +7658,39 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -6579,6 +7700,15 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", @@ -6614,8 +7744,31 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/multistream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/multistream/-/multistream-3.1.0.tgz", + "integrity": "sha512-zBgD3kn8izQAN/TaL1PCMv15vYpf+Vcrsfub06njuYVYlzUldzpopTlrEZ53pZVEbfn3Shtv7vRFoOv6LOV87Q==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^3.4.0" + } + }, + "node_modules/multistream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } }, "node_modules/natural-compare": { "version": "1.4.0", @@ -6628,6 +7781,45 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -6649,6 +7841,18 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -6661,11 +7865,19 @@ "node": ">=8" } }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "dependencies": { "wrappy": "1" } @@ -6702,6 +7914,15 @@ "node": ">= 0.8.0" } }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -6738,6 +7959,63 @@ "node": ">=6" } }, + "node_modules/pac-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", + "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.5", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", @@ -6745,6 +8023,22 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6773,6 +8067,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", + "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6862,37 +8176,168 @@ "node": ">=0.10" } }, - "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true, - "engines": { - "node": ">= 6" + "node_modules/pino": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.5.0.tgz", + "integrity": "sha512-xSEmD4pLnV54t0NOUN16yCl7RIB1c5UUOse5HSyEXtBp+FgFQyPeDutc+Q2ZO7/22vImV7VfEjH/1zV2QuqvYw==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.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" + }, + "bin": { + "pino": "bin.js" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, + "node_modules/pino-abstract-transport": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", + "license": "MIT", "dependencies": { - "find-up": "^4.0.0" + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "node_modules/pino-abstract-transport/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/pkgroll": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/pkgroll/-/pkgroll-2.4.2.tgz", - "integrity": "sha512-9seL/4BNQsE+eL+kefjfh5jSLqQPSKXQE/adw1L76k49KFw/XnOnyU8dRwuWpVtvMyIVyecaSBIpvFYrmnZq6A==", - "dev": true, + "node_modules/pino-abstract-transport/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "license": "MIT", "dependencies": { - "@rollup/plugin-alias": "^5.1.0", - "@rollup/plugin-commonjs": "^26.0.1", - "@rollup/plugin-inject": "^5.0.5", + "safe-buffer": "~5.2.0" + } + }, + "node_modules/pino-pretty": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-11.3.0.tgz", + "integrity": "sha512-oXwn7ICywaZPHmu3epHGU2oJX4nPmKvHvB/bwrJHlGcbEWaVcotkpyVHMKLKmiVryWYByNp0jpgAcXpFJDXJzA==", + "license": "MIT", + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^3.0.2", + "fast-safe-stringify": "^2.1.1", + "help-me": "^5.0.0", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pump": "^3.0.0", + "readable-stream": "^4.0.0", + "secure-json-parse": "^2.4.0", + "sonic-boom": "^4.0.1", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "node_modules/pino-pretty/node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-pretty/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/pino-pretty/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", + "license": "MIT" + }, + "node_modules/pino/node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkgroll": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/pkgroll/-/pkgroll-2.4.2.tgz", + "integrity": "sha512-9seL/4BNQsE+eL+kefjfh5jSLqQPSKXQE/adw1L76k49KFw/XnOnyU8dRwuWpVtvMyIVyecaSBIpvFYrmnZq6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/plugin-alias": "^5.1.0", + "@rollup/plugin-commonjs": "^26.0.1", + "@rollup/plugin-inject": "^5.0.5", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-replace": "^5.0.7", @@ -6970,6 +8415,27 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/process-warning": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.0.tgz", + "integrity": "sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==", + "license": "MIT" + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -6983,6 +8449,86 @@ "node": ">= 6" } }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proxy-agent": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -7029,12 +8575,60 @@ } ] }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7043,6 +8637,15 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -7060,6 +8663,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "license": "MIT" + }, "node_modules/resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", @@ -7098,6 +8707,27 @@ "node": ">=10" } }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -7217,6 +8847,47 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "license": "ISC" + }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "license": "BSD-3-Clause" + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -7226,6 +8897,32 @@ "semver": "bin/semver.js" } }, + "node_modules/sentence-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", + "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, + "node_modules/sequin": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/sequin/-/sequin-0.1.1.tgz", + "integrity": "sha512-hJWMZRwP75ocoBM+1/YaCsvS0j5MTPeBHJkS2/wruehl9xwtX30HlDF1Gt6UZ8HHHY8SJa2/IL+jo+JJCd59rA==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -7250,8 +8947,7 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/sisteransi": { "version": "1.0.5", @@ -7311,6 +9007,75 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", + "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -7329,6 +9094,15 @@ "source-map": "^0.6.0" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -7356,6 +9130,21 @@ "node": ">=8" } }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", @@ -7455,7 +9244,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, "engines": { "node": ">=8" }, @@ -7533,6 +9321,33 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/tldts": { + "version": "6.1.52", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.52.tgz", + "integrity": "sha512-fgrDJXDjbAverY6XnIt0lNfv8A0cf7maTEaZxNykLGsLG7XP+5xhjBTrt/ieAsFjAlZ+G5nmXomLcZDkxXnDzw==", + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.52" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.52", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.52.tgz", + "integrity": "sha512-j4OxQI5rc1Ve/4m/9o2WhWSC4jGc4uVbCINdOEJRAraCi0YqTqgMcxUx7DbmuP0G3PCixoof/RZB0Q5Kh9tagw==", + "license": "MIT" + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -7560,6 +9375,24 @@ "node": ">=8.0" } }, + "node_modules/tough-cookie": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", + "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", @@ -7632,6 +9465,33 @@ "node": ">=10" } }, + "node_modules/ts-retry-promise": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/ts-retry-promise/-/ts-retry-promise-0.8.1.tgz", + "integrity": "sha512-+AHPUmAhr5bSRRK5CurE9kNH8gZlEHnCgusZ0zy2bjfatUBDX0h6vGQjiT0YrGwSDwRZmU+bapeX6mj55FOPvg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tslib": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "license": "0BSD" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -7710,6 +9570,15 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", @@ -7740,6 +9609,24 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/upper-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", + "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/upper-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -7750,6 +9637,12 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/v8-to-istanbul": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", @@ -7779,6 +9672,45 @@ "makeerror": "1.0.12" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -7951,8 +9883,35 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "license": "Apache-2.0" }, "node_modules/y18n": { "version": "5.0.8", diff --git a/package.json b/package.json index 2b7631ed..01e49836 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ }, "dependencies": { "@cparra/apex-reflection": "2.15.0", + "@salesforce/source-deploy-retrieve": "^12.8.1", "@types/js-yaml": "^4.0.9", "@types/yargs": "^17.0.32", "chalk": "^4.1.2", diff --git a/src/application/Apexdocs.ts b/src/application/Apexdocs.ts index 7ee33925..e153bc73 100644 --- a/src/application/Apexdocs.ts +++ b/src/application/Apexdocs.ts @@ -6,11 +6,11 @@ import markdown from './generators/markdown'; import openApi from './generators/openapi'; import changelog from './generators/changelog'; -import { processFiles } from './apex-file-reader'; +import { processFiles } from './source-code-file-reader'; import { DefaultFileSystem } from './file-system'; import { Logger } from '#utils/logger'; import { - UnparsedSourceFile, + UnparsedApexBundle, UserDefinedChangelogConfig, UserDefinedConfig, UserDefinedMarkdownConfig, @@ -18,7 +18,6 @@ import { } from '../core/shared/types'; import { ReflectionError, ReflectionErrors, HookError } from '../core/errors/errors'; import { FileReadingError, FileWritingError } from './errors'; -import { apply } from '#utils/fp'; /** * Application entry-point to generate documentation out of Apex source files. @@ -46,14 +45,19 @@ export class Apexdocs { } } -const readFiles = apply(processFiles, new DefaultFileSystem()); +const readFiles = processFiles(new DefaultFileSystem()); async function processMarkdown(config: UserDefinedMarkdownConfig) { return pipe( - TE.tryCatch( - () => readFiles(config.sourceDir, config.includeMetadata, config.exclude), + E.tryCatch( + () => + readFiles(['ApexClass', 'CustomObject', 'CustomField'], { includeMetadata: config.includeMetadata })( + config.sourceDir, + config.exclude, + ), (e) => new FileReadingError('An error occurred while reading files.', e), ), + TE.fromEither, TE.flatMap((fileBodies) => markdown(fileBodies, config)), TE.map(() => '✔️ Documentation generated successfully!'), TE.mapLeft(toErrors), @@ -61,20 +65,21 @@ async function processMarkdown(config: UserDefinedMarkdownConfig) { } async function processOpenApi(config: UserDefinedOpenApiConfig, logger: Logger) { - const fileBodies = await readFiles(config.sourceDir, false, config.exclude); + const fileBodies = readFiles(['ApexClass'])(config.sourceDir, config.exclude) as UnparsedApexBundle[]; return openApi(logger, fileBodies, config); } async function processChangeLog(config: UserDefinedChangelogConfig) { - async function loadFiles(): Promise<[UnparsedSourceFile[], UnparsedSourceFile[]]> { + function loadFiles(): [UnparsedApexBundle[], UnparsedApexBundle[]] { return [ - await readFiles(config.previousVersionDir, false, config.exclude), - await readFiles(config.currentVersionDir, false, config.exclude), + readFiles(['ApexClass'])(config.previousVersionDir, config.exclude) as UnparsedApexBundle[], + readFiles(['ApexClass'])(config.currentVersionDir, config.exclude) as UnparsedApexBundle[], ]; } return pipe( - TE.tryCatch(loadFiles, (e) => new FileReadingError('An error occurred while reading files.', e)), + E.tryCatch(loadFiles, (e) => new FileReadingError('An error occurred while reading files.', e)), + TE.fromEither, TE.flatMap(([previous, current]) => changelog(previous, current, config)), TE.mapLeft(toErrors), ); diff --git a/src/application/__tests__/apex-file-reader.spec.ts b/src/application/__tests__/apex-file-reader.spec.ts deleted file mode 100644 index 9b33fc5c..00000000 --- a/src/application/__tests__/apex-file-reader.spec.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { FileSystem } from '../file-system'; -import { processFiles } from '../apex-file-reader'; - -type File = { - type: 'file'; - path: string; - content: string; -}; - -type Directory = { - type: 'directory'; - path: string; - files: (File | Directory)[]; -}; - -type Path = File | Directory; - -class TestFileSystem implements FileSystem { - constructor(private readonly paths: Path[]) {} - - async isDirectory(path: string): Promise { - const directory = this.findPath(path); - return directory ? directory.type === 'directory' : false; - } - - joinPath(...paths: string[]): string { - return paths.join('/'); - } - - async readDirectory(sourceDirectory: string): Promise { - const directory = this.findPath(sourceDirectory); - if (!directory || directory.type !== 'directory') { - throw new Error('Directory not found'); - } - return directory.files.map((f) => f.path); - } - - async readFile(path: string): Promise { - const file = this.findPath(path); - if (!file || file.type !== 'file') { - throw new Error('File not found'); - } - return file.content; - } - - exists(path: string): boolean { - return this.paths.some((p) => p.path === path); - } - - findPath(path: string): Path | undefined { - const splitPath = path.split('/'); - let currentPath = this.paths.find((p) => p.path === splitPath[0]); - for (let i = 1; i < splitPath.length; i++) { - if (!currentPath || currentPath.type !== 'directory') { - return undefined; - } - currentPath = currentPath.files.find((f) => f.path === splitPath[i]); - } - return currentPath; - } -} - -describe('File Reader', () => { - it('returns an empty list when there are no files in the directory', async () => { - const fileSystem = new TestFileSystem([ - { - type: 'directory', - path: '', - files: [], - }, - ]); - - const result = await processFiles(fileSystem, '', false, []); - - expect(result.length).toBe(0); - }); - - it('returns an empty list when there are no Apex files in the directory', async () => { - const fileSystem = new TestFileSystem([ - { - type: 'directory', - path: '', - files: [ - { - type: 'file', - path: 'SomeFile.md', - content: '## Some Markdown', - }, - ], - }, - ]); - - const result = await processFiles(fileSystem, '', false, []); - expect(result.length).toBe(0); - }); - - it('returns the file contents for all Apex files', async () => { - const fileSystem = new TestFileSystem([ - { - type: 'directory', - path: '', - files: [ - { - type: 'file', - path: 'SomeFile.cls', - content: 'public class MyClass{}', - }, - { - type: 'directory', - path: 'subdir', - files: [ - { - type: 'file', - path: 'AnotherFile.cls', - content: 'public class AnotherClass{}', - }, - ], - }, - ], - }, - ]); - - const result = await processFiles(fileSystem, '', false, []); - expect(result.length).toBe(2); - expect(result[0].content).toBe('public class MyClass{}'); - expect(result[1].content).toBe('public class AnotherClass{}'); - }); - - it('skips files that match the excluded glob pattern', async () => { - const fileSystem = new TestFileSystem([ - { - type: 'directory', - path: '', - files: [ - { - type: 'file', - path: 'SomeFile.cls', - content: 'public class MyClass{}', - }, - { - type: 'directory', - path: 'subdir', - files: [ - { - type: 'file', - path: 'AnotherFile.cls', - content: 'public class AnotherClass{}', - }, - ], - }, - ], - }, - ]); - - const result = await processFiles(fileSystem, '', false, ['**/AnotherFile.cls']); - expect(result.length).toBe(1); - expect(result[0].content).toBe('public class MyClass{}'); - }); - - it('returns the file contents for all Apex when there are multiple directories', async () => { - const fileSystem = new TestFileSystem([ - { - type: 'directory', - path: '', - files: [ - { - type: 'file', - path: 'SomeFile.cls', - content: 'public class MyClass{}', - }, - { - type: 'directory', - path: 'subdir', - files: [ - { - type: 'file', - path: 'AnotherFile.cls', - content: 'public class AnotherClass{}', - }, - ], - }, - { - type: 'directory', - path: 'subdir2', - files: [ - { - type: 'file', - path: 'SomeFile2.cls', - content: 'public class MyClass{}', - }, - { - type: 'directory', - path: 'subdir', - files: [ - { - type: 'file', - path: 'AnotherFile2.cls', - content: 'public class AnotherClass{}', - }, - ], - }, - ], - }, - ], - }, - ]); - - const result = await processFiles(fileSystem, '', false, []); - expect(result.length).toBe(4); - }); -}); diff --git a/src/application/__tests__/source-code-file-reader.spec.ts b/src/application/__tests__/source-code-file-reader.spec.ts new file mode 100644 index 00000000..7a24e7f0 --- /dev/null +++ b/src/application/__tests__/source-code-file-reader.spec.ts @@ -0,0 +1,130 @@ +import { FileSystem } from '../file-system'; +import { processFiles, SourceComponentAdapter } from '../source-code-file-reader'; + +class TestFileSystem implements FileSystem { + constructor(private readonly sourceComponents: SourceComponentAdapter[]) {} + + getComponents(): SourceComponentAdapter[] { + return this.sourceComponents; + } + + readFile(path: string): string { + switch (path) { + case 'Speaker.cls': + return 'public class Speaker{}'; + case 'AnotherSpeaker.cls': + return 'public class AnotherSpeaker{}'; + case 'SomeObject__c.object-meta.xml': + return ` + + + Deployed + test object for testing + + MyFirstObjects + `; + default: + return ''; + } + } +} + +describe('File Reader', () => { + it('returns an empty list when no source components are found', async () => { + const fileSystem = new TestFileSystem([]); + + const result = processFiles(fileSystem)(['ApexClass'])('', []); + + expect(result.length).toBe(0); + }); + + it('returns an empty list when reading Apex files and there are none', async () => { + const fileSystem = new TestFileSystem([ + { + name: 'Speaker__c', + type: { + id: 'customobject', + name: 'CustomObject', + }, + xml: 'force-app/main/default/objects/Speaker__c/Speaker__c.object-meta.xml', + content: 'force-app/main/default/objects/Speaker__c', + }, + ]); + + const result = processFiles(fileSystem)(['ApexClass'])('', []); + expect(result.length).toBe(0); + }); + + it('returns the file contents for all Apex files', async () => { + const fileSystem = new TestFileSystem([ + { + name: 'Speaker', + type: { + id: 'apexclass', + name: 'ApexClass', + }, + xml: 'Speaker.cls-meta.xml', + content: 'Speaker.cls', + }, + { + name: 'AnotherSpeaker', + type: { + id: 'apexclass', + name: 'ApexClass', + }, + xml: 'AnotherSpeaker.cls-meta.xml', + content: 'AnotherSpeaker.cls', + }, + ]); + + const result = processFiles(fileSystem)(['ApexClass'])('', []); + expect(result.length).toBe(2); + expect(result[0].content).toBe('public class Speaker{}'); + expect(result[1].content).toBe('public class AnotherSpeaker{}'); + }); + + it('returns the file contents of all Object files', async () => { + const fileSystem = new TestFileSystem([ + { + name: 'SomeObject__c', + type: { + id: 'customobject', + name: 'CustomObject', + }, + xml: 'SomeObject__c.object-meta.xml', + content: '', + }, + ]); + + const result = processFiles(fileSystem)(['CustomObject'])('', []); + expect(result.length).toBe(1); + expect(result[0].content).toContain('test object for testing'); + }); + + it('skips files that match the excluded glob pattern', async () => { + const fileSystem = new TestFileSystem([ + { + name: 'Speaker', + type: { + id: 'apexclass', + name: 'ApexClass', + }, + xml: 'Speaker.cls-meta.xml', + content: 'Speaker.cls', + }, + { + name: 'AnotherSpeaker', + type: { + id: 'apexclass', + name: 'ApexClass', + }, + xml: 'AnotherSpeaker.cls-meta.xml', + content: 'AnotherSpeaker.cls', + }, + ]); + + const result = processFiles(fileSystem)(['ApexClass'])('', ['**/Speaker.cls']); + expect(result.length).toBe(1); + expect(result[0].content).toBe('public class AnotherSpeaker{}'); + }); +}); diff --git a/src/application/apex-file-reader.ts b/src/application/apex-file-reader.ts deleted file mode 100644 index 4ddd3697..00000000 --- a/src/application/apex-file-reader.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { FileSystem } from './file-system'; -import { UnparsedSourceFile } from '../core/shared/types'; -import { minimatch } from 'minimatch'; -import { pipe } from 'fp-ts/function'; -import { apply } from '#utils/fp'; - -const APEX_FILE_EXTENSION = '.cls'; - -/** - * Reads from .cls files and returns their raw body. - */ -export async function processFiles( - fileSystem: FileSystem, - rootPath: string, - includeMetadata: boolean, - exclude: string[], -): Promise { - const processSingleFile = apply(processFile, fileSystem, includeMetadata); - - return pipe( - await getFilePaths(fileSystem, rootPath), - (filePaths) => filePaths.filter((filePath) => !isExcluded(filePath, exclude)), - (filePaths) => filePaths.filter(isApexFile), - (filePaths) => Promise.all(filePaths.map(processSingleFile)), - ); -} - -async function getFilePaths(fileSystem: FileSystem, rootPath: string): Promise { - const directoryContents = await fileSystem.readDirectory(rootPath); - const paths: string[] = []; - for (const filePath of directoryContents) { - const currentPath = fileSystem.joinPath(rootPath, filePath); - if (await fileSystem.isDirectory(currentPath)) { - paths.push(...(await getFilePaths(fileSystem, currentPath))); - } else { - paths.push(currentPath); - } - } - return paths; -} - -function isExcluded(filePath: string, exclude: string[]): boolean { - return exclude.some((pattern) => minimatch(filePath, pattern)); -} - -async function processFile( - fileSystem: FileSystem, - includeMetadata: boolean, - filePath: string, -): Promise { - const rawTypeContent = await fileSystem.readFile(filePath); - const metadataPath = `${filePath}-meta.xml`; - let rawMetadataContent = null; - if (includeMetadata) { - rawMetadataContent = fileSystem.exists(metadataPath) ? await fileSystem.readFile(metadataPath) : null; - } - - return { filePath, content: rawTypeContent, metadataContent: rawMetadataContent }; -} - -function isApexFile(currentFile: string): boolean { - return currentFile.endsWith(APEX_FILE_EXTENSION); -} diff --git a/src/application/file-system.ts b/src/application/file-system.ts index 3626bf1d..e45b401c 100644 --- a/src/application/file-system.ts +++ b/src/application/file-system.ts @@ -1,69 +1,38 @@ import * as fs from 'fs'; -import * as path from 'path'; +import { MetadataResolver } from '@salesforce/source-deploy-retrieve'; +import { SourceComponentAdapter } from './source-code-file-reader'; +import { pipe } from 'fp-ts/function'; +import * as nodePath from 'path'; export interface FileSystem { - isDirectory: (path: string) => Promise; - readDirectory: (sourceDirectory: string) => Promise; - readFile: (path: string) => Promise; - joinPath: (...paths: string[]) => string; - exists: (path: string) => boolean; -} - -function stat(path: string): Promise { - return new Promise((resolve, reject) => { - fs.stat(path, (err, stats) => { - if (err) { - reject(err); - } else { - resolve(stats); - } - }); - }); -} - -function readdir(path: string): Promise { - return new Promise((resolve, reject) => { - fs.readdir(path, (err, files) => { - if (err) { - reject(err); - } else { - resolve(files); - } - }); - }); -} - -function readFile(path: string): Promise { - return new Promise((resolve, reject) => { - fs.readFile(path, (err, data) => { - if (err) { - reject(err); - } else { - resolve(data.toString()); - } - }); - }); + getComponents(path: string): SourceComponentAdapter[]; + readFile: (path: string) => string; } export class DefaultFileSystem implements FileSystem { - async isDirectory(pathToRead: string): Promise { - const stats = await stat(pathToRead); - return stats.isDirectory(); - } - - readDirectory(sourceDirectory: string): Promise { - return readdir(sourceDirectory); - } - - readFile(pathToRead: string): Promise { - return readFile(pathToRead); - } - - joinPath(...paths: string[]): string { - return path.join(...paths); + getComponents(path: string): SourceComponentAdapter[] { + const components = new MetadataResolver().getComponentsFromPath(path); + + const fieldComponents = pipe( + components, + (components) => components.filter((component) => component.type.name === 'CustomObject'), + (components) => components.map((component) => component.content), + (contents) => contents.filter((content) => content !== undefined), + (contents) => contents.map((content) => nodePath.join(content!, 'fields')), + (potentialFieldLocations) => + potentialFieldLocations.filter((potentialFieldLocation) => fs.existsSync(potentialFieldLocation)), + (potentialFieldLocations) => + potentialFieldLocations.map((potentialFieldLocation) => + new MetadataResolver().getComponentsFromPath(potentialFieldLocation), + ), + (fieldComponents) => fieldComponents.flat(), + (fieldComponents) => fieldComponents.filter((fieldComponent) => fieldComponent.type.name === 'CustomField'), + ); + + return [...components, ...fieldComponents]; } - exists(path: string): boolean { - return fs.existsSync(path); + readFile(pathToRead: string): string { + return fs.readFileSync(pathToRead, 'utf8'); } } diff --git a/src/application/generators/changelog.ts b/src/application/generators/changelog.ts index 8556deb2..d72dd1f9 100644 --- a/src/application/generators/changelog.ts +++ b/src/application/generators/changelog.ts @@ -1,5 +1,5 @@ import { pipe } from 'fp-ts/function'; -import { PageData, Skip, UnparsedSourceFile, UserDefinedChangelogConfig } from '../../core/shared/types'; +import { PageData, Skip, UnparsedApexBundle, UserDefinedChangelogConfig } from '../../core/shared/types'; import * as TE from 'fp-ts/TaskEither'; import { writeFiles } from '../file-writer'; import { ChangeLogPageData, generateChangeLog } from '../../core/changelog/generate-change-log'; @@ -7,8 +7,8 @@ import { FileWritingError } from '../errors'; import { isSkip } from '../../core/shared/utils'; export default function generate( - oldBundles: UnparsedSourceFile[], - newBundles: UnparsedSourceFile[], + oldBundles: UnparsedApexBundle[], + newBundles: UnparsedApexBundle[], config: UserDefinedChangelogConfig, ) { function handleFile(file: ChangeLogPageData | Skip) { diff --git a/src/application/generators/markdown.ts b/src/application/generators/markdown.ts index 50fa766a..56f2e9e1 100644 --- a/src/application/generators/markdown.ts +++ b/src/application/generators/markdown.ts @@ -3,7 +3,7 @@ import { pipe } from 'fp-ts/function'; import { PageData, PostHookDocumentationBundle, - UnparsedSourceFile, + UnparsedSourceBundle, UserDefinedMarkdownConfig, } from '../../core/shared/types'; import { referenceGuideTemplate } from '../../core/markdown/templates/reference-guide'; @@ -12,14 +12,14 @@ import { isSkip } from '../../core/shared/utils'; import { writeFiles } from '../file-writer'; import { FileWritingError } from '../errors'; -export default function generate(bundles: UnparsedSourceFile[], config: UserDefinedMarkdownConfig) { +export default function generate(bundles: UnparsedSourceBundle[], config: UserDefinedMarkdownConfig) { return pipe( generateDocumentationBundle(bundles, config), TE.flatMap((files) => writeFilesToSystem(files, config.targetDir)), ); } -function generateDocumentationBundle(bundles: UnparsedSourceFile[], config: UserDefinedMarkdownConfig) { +function generateDocumentationBundle(bundles: UnparsedSourceBundle[], config: UserDefinedMarkdownConfig) { return generateDocs(bundles, { ...config, referenceGuideTemplate: referenceGuideTemplate, diff --git a/src/application/generators/openapi.ts b/src/application/generators/openapi.ts index 8a15d2cd..79ecf7ad 100644 --- a/src/application/generators/openapi.ts +++ b/src/application/generators/openapi.ts @@ -6,17 +6,17 @@ import { Logger } from '#utils/logger'; import ErrorLogger from '#utils/error-logger'; import { reflect, ReflectionResult } from '@cparra/apex-reflection'; import Manifest from '../../core/manifest'; -import { PageData, UnparsedSourceFile, UserDefinedOpenApiConfig } from '../../core/shared/types'; +import { PageData, UnparsedApexBundle, UserDefinedOpenApiConfig } from '../../core/shared/types'; import { OpenApiDocsProcessor } from '../../core/openapi/open-api-docs-processor'; import { writeFiles } from '../file-writer'; import { pipe } from 'fp-ts/function'; import * as TE from 'fp-ts/TaskEither'; -import { OpenApiSettings } from '../../core/openApiSettings'; +import { OpenApiSettings } from '../../core/openapi/openApiSettings'; import { apply } from '#utils/fp'; export default async function openApi( logger: Logger, - fileBodies: UnparsedSourceFile[], + fileBodies: UnparsedApexBundle[], config: UserDefinedOpenApiConfig, ) { OpenApiSettings.build({ @@ -46,7 +46,7 @@ export default async function openApi( ErrorLogger.logErrors(logger, filteredTypes); } -function reflectionWithLogger(logger: Logger, apexBundle: UnparsedSourceFile): ReflectionResult { +function reflectionWithLogger(logger: Logger, apexBundle: UnparsedApexBundle): ReflectionResult { const result = reflect(apexBundle.content); if (result.error) { logger.error(`${apexBundle.filePath} - Parsing error ${result.error?.message}`); diff --git a/src/application/source-code-file-reader.ts b/src/application/source-code-file-reader.ts new file mode 100644 index 00000000..7d9b5da3 --- /dev/null +++ b/src/application/source-code-file-reader.ts @@ -0,0 +1,168 @@ +import { FileSystem } from './file-system'; +import { UnparsedApexBundle, UnparsedCustomFieldBundle, UnparsedCustomObjectBundle } from '../core/shared/types'; +import { minimatch } from 'minimatch'; +import { flow, pipe } from 'fp-ts/function'; +import { apply } from '#utils/fp'; + +type ComponentTypes = 'ApexClass' | 'CustomObject' | 'CustomField'; + +/** + * Simplified representation of a source component, with only + * the required information we need. + */ +export type SourceComponentAdapter = { + name: string; + type: { + id: string; + name: string; + }; + xml?: string; + content?: string; + parent?: { + name: string; + }; +}; + +type ApexClassApexSourceComponent = { + type: 'ApexClass'; + name: string; + xmlPath?: string; + contentPath: string; +}; + +type CustomObjectSourceComponent = { + type: 'CustomObject'; + name: string; + contentPath: string; +}; + +type CustomFieldSourceComponent = { + type: 'CustomField'; + name: string; + contentPath: string; + parentName: string; +}; + +function getApexSourceComponents( + includeMetadata: boolean, + sourceComponents: SourceComponentAdapter[], +): ApexClassApexSourceComponent[] { + return sourceComponents + .filter((component) => component.type.name === 'ApexClass') + .map((component) => ({ + type: 'ApexClass' as const, + name: component.name, + xmlPath: includeMetadata ? component.xml : undefined, + contentPath: component.content!, + })); +} + +function toUnparsedApexBundle( + fileSystem: FileSystem, + apexSourceComponents: ApexClassApexSourceComponent[], +): UnparsedApexBundle[] { + return apexSourceComponents.map((component) => { + const apexComponentTuple: [string, string | null] = [ + fileSystem.readFile(component.contentPath), + component.xmlPath ? fileSystem.readFile(component.xmlPath) : null, + ]; + + return { + type: 'apex', + name: component.name, + filePath: component.contentPath, + content: apexComponentTuple[0], + metadataContent: apexComponentTuple[1], + }; + }); +} + +function getCustomObjectSourceComponents(sourceComponents: SourceComponentAdapter[]): CustomObjectSourceComponent[] { + return sourceComponents + .filter((component) => component.type.name === 'CustomObject') + .map((component) => ({ + name: component.name, + type: 'CustomObject' as const, + contentPath: component.xml!, + })); +} + +function toUnparsedSObjectBundle( + fileSystem: FileSystem, + customObjectSourceComponents: CustomObjectSourceComponent[], +): UnparsedCustomObjectBundle[] { + return customObjectSourceComponents.map((component) => { + return { + type: 'customobject', + name: component.name, + filePath: component.contentPath, + content: fileSystem.readFile(component.contentPath), + }; + }); +} + +function getCustomFieldSourceComponents(sourceComponents: SourceComponentAdapter[]): CustomFieldSourceComponent[] { + return sourceComponents + .filter((component) => component.type.name === 'CustomField') + .map((component) => ({ + name: component.name, + type: 'CustomField' as const, + contentPath: component.xml!, + parentName: component.parent!.name, + })); +} + +function toUnparsedCustomFieldBundle( + fileSystem: FileSystem, + customFieldSourceComponents: CustomFieldSourceComponent[], +): UnparsedCustomFieldBundle[] { + return customFieldSourceComponents.map((component) => ({ + type: 'customfield', + name: component.name, + filePath: component.contentPath, + content: fileSystem.readFile(component.contentPath), + parentName: component.parentName, + })); +} + +/** + * Reads from source code files and returns their raw body. + */ +export function processFiles(fileSystem: FileSystem) { + return ( + componentTypesToRetrieve: T, + options: { includeMetadata: boolean } = { includeMetadata: false }, + ) => { + const converters: Record< + ComponentTypes, + ( + components: SourceComponentAdapter[], + ) => (UnparsedApexBundle | UnparsedCustomObjectBundle | UnparsedCustomFieldBundle)[] + > = { + ApexClass: flow(apply(getApexSourceComponents, options.includeMetadata), (apexSourceComponents) => + toUnparsedApexBundle(fileSystem, apexSourceComponents), + ), + CustomObject: flow(getCustomObjectSourceComponents, (customObjectSourceComponents) => + toUnparsedSObjectBundle(fileSystem, customObjectSourceComponents), + ), + CustomField: flow(getCustomFieldSourceComponents, (customFieldSourceComponents) => + toUnparsedCustomFieldBundle(fileSystem, customFieldSourceComponents), + ), + }; + + const convertersToUse = componentTypesToRetrieve.map((componentType) => converters[componentType]); + + return (rootPath: string, exclude: string[]) => { + return pipe( + fileSystem.getComponents(rootPath), + (components) => components.filter((component) => !isExcluded(component.content!, exclude)), + (components) => convertersToUse.map((converter) => converter(components)), + (bundles) => bundles.flat(), + ); + }; + }; +} + +function isExcluded(filePath: string, exclude: string[]): boolean { + return exclude.some((pattern) => minimatch(filePath, pattern)); +} diff --git a/src/cli/args.ts b/src/cli/args.ts index 52e38f74..9f6cedc5 100644 --- a/src/cli/args.ts +++ b/src/cli/args.ts @@ -122,7 +122,6 @@ function extractArgsForCommandsProvidedInConfig( return pipe( extractMultiCommandConfig(extractFromProcessFn, 'markdown', generatorConfig), E.map((cliArgs) => { - console.log('markdown', cliArgs); return cliArgs; }), E.map((cliArgs) => ({ ...configOnlyMarkdownDefaults, ...generatorConfig, ...cliArgs })), @@ -136,7 +135,6 @@ function extractArgsForCommandsProvidedInConfig( return pipe( extractMultiCommandConfig(extractFromProcessFn, 'changelog', generatorConfig), E.map((cliArgs) => { - console.log('changelog', cliArgs); return cliArgs; }), E.map((cliArgs) => ({ ...configOnlyChangelogDefaults, ...generatorConfig, ...cliArgs })), @@ -224,7 +222,6 @@ function extractMultiCommandConfig( } const options = getOptions(command); - console.log('config', config); return E.tryCatch(() => { return yargs(extractFromProcessFn()) .config(config) diff --git a/src/cli/commands/markdown.ts b/src/cli/commands/markdown.ts index 46cfb46b..b8531a9f 100644 --- a/src/cli/commands/markdown.ts +++ b/src/cli/commands/markdown.ts @@ -28,6 +28,11 @@ export const markdownOptions: { [key: string]: Options } = { default: markdownDefaults.defaultGroupName, describe: 'Defines the @group name to be used when a file does not specify it.', }, + customObjectGroupName: { + type: 'string', + default: markdownDefaults.customObjectsGroupName, + describe: 'The name under which custom objects will be grouped in the Reference Guide', + }, namespace: { type: 'string', describe: 'The package namespace, if any. If provided, it will be added to the generated files.', diff --git a/src/core/changelog/__test__/generating-change-log.spec.ts b/src/core/changelog/__test__/generating-change-log.spec.ts index 55c9bf33..28e9512e 100644 --- a/src/core/changelog/__test__/generating-change-log.spec.ts +++ b/src/core/changelog/__test__/generating-change-log.spec.ts @@ -1,4 +1,4 @@ -import { UnparsedSourceFile } from '../../shared/types'; +import { UnparsedApexBundle } from '../../shared/types'; import { ChangeLogPageData, generateChangeLog } from '../generate-change-log'; import { assertEither } from '../../test-helpers/assert-either'; import { isSkip } from '../../shared/utils'; @@ -34,8 +34,8 @@ describe('when generating a changelog', () => { describe('that does not include new classes', () => { it('should not have a section for new classes', async () => { - const oldBundle: UnparsedSourceFile[] = []; - const newBundle: UnparsedSourceFile[] = []; + const oldBundle: UnparsedApexBundle[] = []; + const newBundle: UnparsedApexBundle[] = []; const result = await generateChangeLog(oldBundle, newBundle, config)(); @@ -47,9 +47,9 @@ describe('when generating a changelog', () => { it('should include a section for new classes', async () => { const newClassSource = 'class Test {}'; - const oldBundle: UnparsedSourceFile[] = []; - const newBundle: UnparsedSourceFile[] = [ - { content: newClassSource, filePath: 'Test.cls', metadataContent: null }, + const oldBundle: UnparsedApexBundle[] = []; + const newBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: newClassSource, filePath: 'Test.cls', metadataContent: null }, ]; const result = await generateChangeLog(oldBundle, newBundle, config)(); @@ -60,9 +60,9 @@ describe('when generating a changelog', () => { it('should include the new class name', async () => { const newClassSource = 'class Test {}'; - const oldBundle: UnparsedSourceFile[] = []; - const newBundle: UnparsedSourceFile[] = [ - { content: newClassSource, filePath: 'Test.cls', metadataContent: null }, + const oldBundle: UnparsedApexBundle[] = []; + const newBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: newClassSource, filePath: 'Test.cls', metadataContent: null }, ]; const result = await generateChangeLog(oldBundle, newBundle, config)(); @@ -78,9 +78,9 @@ describe('when generating a changelog', () => { class Test {} `; - const oldBundle: UnparsedSourceFile[] = []; - const newBundle: UnparsedSourceFile[] = [ - { content: newClassSource, filePath: 'Test.cls', metadataContent: null }, + const oldBundle: UnparsedApexBundle[] = []; + const newBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: newClassSource, filePath: 'Test.cls', metadataContent: null }, ]; const result = await generateChangeLog(oldBundle, newBundle, config)(); @@ -93,9 +93,9 @@ describe('when generating a changelog', () => { it('should include a section for new interfaces', async () => { const newInterfaceSource = 'interface Test {}'; - const oldBundle: UnparsedSourceFile[] = []; - const newBundle: UnparsedSourceFile[] = [ - { content: newInterfaceSource, filePath: 'Test.cls', metadataContent: null }, + const oldBundle: UnparsedApexBundle[] = []; + const newBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: newInterfaceSource, filePath: 'Test.cls', metadataContent: null }, ]; const result = await generateChangeLog(oldBundle, newBundle, config)(); @@ -106,9 +106,9 @@ describe('when generating a changelog', () => { it('should include the new interface name', async () => { const newInterfaceSource = 'interface Test {}'; - const oldBundle: UnparsedSourceFile[] = []; - const newBundle: UnparsedSourceFile[] = [ - { content: newInterfaceSource, filePath: 'Test.cls', metadataContent: null }, + const oldBundle: UnparsedApexBundle[] = []; + const newBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: newInterfaceSource, filePath: 'Test.cls', metadataContent: null }, ]; const result = await generateChangeLog(oldBundle, newBundle, config)(); @@ -124,9 +124,9 @@ describe('when generating a changelog', () => { interface Test {} `; - const oldBundle: UnparsedSourceFile[] = []; - const newBundle: UnparsedSourceFile[] = [ - { content: newInterfaceSource, filePath: 'Test.cls', metadataContent: null }, + const oldBundle: UnparsedApexBundle[] = []; + const newBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: newInterfaceSource, filePath: 'Test.cls', metadataContent: null }, ]; const result = await generateChangeLog(oldBundle, newBundle, config)(); @@ -141,8 +141,10 @@ describe('when generating a changelog', () => { it('should include a section for new enums', async () => { const newEnumSource = 'enum Test {}'; - const oldBundle: UnparsedSourceFile[] = []; - const newBundle: UnparsedSourceFile[] = [{ content: newEnumSource, filePath: 'Test.cls', metadataContent: null }]; + const oldBundle: UnparsedApexBundle[] = []; + const newBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: newEnumSource, filePath: 'Test.cls', metadataContent: null }, + ]; const result = await generateChangeLog(oldBundle, newBundle, config)(); @@ -152,8 +154,10 @@ describe('when generating a changelog', () => { it('should include the new enum name', async () => { const newEnumSource = 'enum Test {}'; - const oldBundle: UnparsedSourceFile[] = []; - const newBundle: UnparsedSourceFile[] = [{ content: newEnumSource, filePath: 'Test.cls', metadataContent: null }]; + const oldBundle: UnparsedApexBundle[] = []; + const newBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: newEnumSource, filePath: 'Test.cls', metadataContent: null }, + ]; const result = await generateChangeLog(oldBundle, newBundle, config)(); @@ -168,8 +172,10 @@ describe('when generating a changelog', () => { enum Test {} `; - const oldBundle: UnparsedSourceFile[] = []; - const newBundle: UnparsedSourceFile[] = [{ content: newEnumSource, filePath: 'Test.cls', metadataContent: null }]; + const oldBundle: UnparsedApexBundle[] = []; + const newBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: newEnumSource, filePath: 'Test.cls', metadataContent: null }, + ]; const result = await generateChangeLog(oldBundle, newBundle, config)(); @@ -181,9 +187,9 @@ describe('when generating a changelog', () => { it('should not include them', async () => { const newClassSource = 'class Test {}'; - const oldBundle: UnparsedSourceFile[] = []; - const newBundle: UnparsedSourceFile[] = [ - { content: newClassSource, filePath: 'Test.cls', metadataContent: null }, + const oldBundle: UnparsedApexBundle[] = []; + const newBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: newClassSource, filePath: 'Test.cls', metadataContent: null }, ]; const result = await generateChangeLog(oldBundle, newBundle, { ...config, scope: ['global'] })(); @@ -196,10 +202,10 @@ describe('when generating a changelog', () => { it('should include a section for removed types', async () => { const oldClassSource = 'class Test {}'; - const oldBundle: UnparsedSourceFile[] = [ - { content: oldClassSource, filePath: 'Test.cls', metadataContent: null }, + const oldBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: oldClassSource, filePath: 'Test.cls', metadataContent: null }, ]; - const newBundle: UnparsedSourceFile[] = []; + const newBundle: UnparsedApexBundle[] = []; const result = await generateChangeLog(oldBundle, newBundle, config)(); @@ -209,10 +215,10 @@ describe('when generating a changelog', () => { it('should include the removed type name', async () => { const oldClassSource = 'class Test {}'; - const oldBundle: UnparsedSourceFile[] = [ - { content: oldClassSource, filePath: 'Test.cls', metadataContent: null }, + const oldBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: oldClassSource, filePath: 'Test.cls', metadataContent: null }, ]; - const newBundle: UnparsedSourceFile[] = []; + const newBundle: UnparsedApexBundle[] = []; const result = await generateChangeLog(oldBundle, newBundle, config)(); @@ -225,12 +231,12 @@ describe('when generating a changelog', () => { const oldClassSource = 'class Test {}'; const newClassSource = 'class Test { void myMethod() {} }'; - const oldBundle: UnparsedSourceFile[] = [ - { content: oldClassSource, filePath: 'Test.cls', metadataContent: null }, + const oldBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: oldClassSource, filePath: 'Test.cls', metadataContent: null }, ]; - const newBundle: UnparsedSourceFile[] = [ - { content: newClassSource, filePath: 'Test.cls', metadataContent: null }, + const newBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: newClassSource, filePath: 'Test.cls', metadataContent: null }, ]; const result = await generateChangeLog(oldBundle, newBundle, config)(); @@ -244,12 +250,12 @@ describe('when generating a changelog', () => { const oldClassSource = 'class Test {}'; const newClassSource = 'class Test { void myMethod() {} }'; - const oldBundle: UnparsedSourceFile[] = [ - { content: oldClassSource, filePath: 'Test.cls', metadataContent: null }, + const oldBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: oldClassSource, filePath: 'Test.cls', metadataContent: null }, ]; - const newBundle: UnparsedSourceFile[] = [ - { content: newClassSource, filePath: 'Test.cls', metadataContent: null }, + const newBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: newClassSource, filePath: 'Test.cls', metadataContent: null }, ]; const result = await generateChangeLog(oldBundle, newBundle, config)(); @@ -261,12 +267,12 @@ describe('when generating a changelog', () => { const oldClassSource = 'class Test {}'; const newClassSource = 'class Test { void myMethod() {} }'; - const oldBundle: UnparsedSourceFile[] = [ - { content: oldClassSource, filePath: 'Test.cls', metadataContent: null }, + const oldBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: oldClassSource, filePath: 'Test.cls', metadataContent: null }, ]; - const newBundle: UnparsedSourceFile[] = [ - { content: newClassSource, filePath: 'Test.cls', metadataContent: null }, + const newBundle: UnparsedApexBundle[] = [ + { type: 'apex', name: 'Test', content: newClassSource, filePath: 'Test.cls', metadataContent: null }, ]; const result = await generateChangeLog(oldBundle, newBundle, config)(); diff --git a/src/core/changelog/generate-change-log.ts b/src/core/changelog/generate-change-log.ts index 371073ca..9552308f 100644 --- a/src/core/changelog/generate-change-log.ts +++ b/src/core/changelog/generate-change-log.ts @@ -1,15 +1,15 @@ -import { ParsedFile, Skip, UnparsedSourceFile, UserDefinedChangelogConfig } from '../shared/types'; +import { ParsedFile, Skip, UnparsedApexBundle, UserDefinedChangelogConfig } from '../shared/types'; import { pipe } from 'fp-ts/function'; import * as TE from 'fp-ts/TaskEither'; -import { reflectBundles } from '../reflection/reflect-source'; +import { reflectApexSource } from '../reflection/apex/reflect-apex-source'; import { Changelog, hasChanges, processChangelog, VersionManifest } from './process-changelog'; import { convertToRenderableChangelog, RenderableChangelog } from './renderable-changelog'; import { CompilationRequest, Template } from '../template'; import { changelogTemplate } from './templates/changelog-template'; import { ReflectionErrors } from '../errors/errors'; import { apply } from '#utils/fp'; -import { filterScope } from '../reflection/filter-scope'; -import { skip } from '../shared/utils'; +import { filterScope } from '../reflection/apex/filter-scope'; +import { isApexType, skip } from '../shared/utils'; export type ChangeLogPageData = { content: string; @@ -17,14 +17,14 @@ export type ChangeLogPageData = { }; export function generateChangeLog( - oldBundles: UnparsedSourceFile[], - newBundles: UnparsedSourceFile[], + oldBundles: UnparsedApexBundle[], + newBundles: UnparsedApexBundle[], config: Omit, ): TE.TaskEither { const filterOutOfScope = apply(filterScope, config.scope); - function reflect(sourceFiles: UnparsedSourceFile[]) { - return pipe(reflectBundles(sourceFiles), TE.map(filterOutOfScope)); + function reflect(sourceFiles: UnparsedApexBundle[]) { + return pipe(reflectApexSource(sourceFiles), TE.map(filterOutOfScope)); } const convertToPageData = apply(toPageData, config.fileName); @@ -52,7 +52,10 @@ export function generateChangeLog( function toManifests({ oldVersion, newVersion }: { oldVersion: ParsedFile[]; newVersion: ParsedFile[] }) { function parsedFilesToManifest(parsedFiles: ParsedFile[]): VersionManifest { return { - types: parsedFiles.map((parsedFile) => parsedFile.type), + types: parsedFiles + .map((parsedFile) => parsedFile.type) + // Changelog does not currently support object types + .filter((type) => isApexType(type)), }; } diff --git a/src/core/markdown/__test__/generating-any-apex-doc.spec.ts b/src/core/markdown/__test__/generating-any-apex-doc.spec.ts new file mode 100644 index 00000000..44ce4404 --- /dev/null +++ b/src/core/markdown/__test__/generating-any-apex-doc.spec.ts @@ -0,0 +1,269 @@ +import { extendExpect } from './expect-extensions'; +import { unparsedApexBundleFromRawString, generateDocs } from './test-helpers'; +import { assertEither } from '../../test-helpers/assert-either'; + +describe('When generating documentation', () => { + beforeAll(() => { + extendExpect(); + }); + + describe('the documentation content', () => { + it('displays type level annotations', async () => { + const input = ` + @NamespaceAccessible + public class MyClass { + @Deprecated + public void myMethod() {} + } + `; + + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); + + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('NAMESPACEACCESSIBLE')); + assertEither(result, (data) => expect(data).firstDocContains('DEPRECATED')); + }); + + it('displays metadata as annotations', async () => { + const input = 'public class MyClass {}'; + const metadata = ` + + + 59.0 + Active + + `; + + const result = await generateDocs([unparsedApexBundleFromRawString(input, metadata)])(); + + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('APIVERSION')); + assertEither(result, (data) => expect(data).firstDocContains('STATUS')); + }); + + it('displays the description when no @description tag is used', async () => { + const input = ` + /** + * This is a description + */ + public class MyClass {} + `; + + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); + + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('This is a description')); + }); + + it('displays the description when a @description tag is used', async () => { + const input = ` + /** + * @description This is a description + */ + public class MyClass {}`; + + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('This is a description')); + }); + + it('display custom documentation tags', async () => { + const input = ` + /** + * @custom-tag My Value + */ + public class MyClass {} + `; + + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('Custom Tag')); + assertEither(result, (data) => expect(data).firstDocContains('My Value')); + }); + + it('displays the group', async () => { + const input = ` + /** + * @group MyGroup + */ + public class MyClass {}`; + + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('Group')); + assertEither(result, (data) => expect(data).firstDocContains('MyGroup')); + }); + + it('displays the author', async () => { + const input = ` + /** + * @author John Doe + */ + public class MyClass {}`; + + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('Author')); + assertEither(result, (data) => expect(data).firstDocContains('John Doe')); + }); + + it('displays the date', async () => { + const input = ` + /** + * @date 2021-01-01 + */ + public class MyClass {}`; + + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('Date')); + assertEither(result, (data) => expect(data).firstDocContains('2021-01-01')); + }); + + it('displays descriptions with links', async () => { + const input1 = ` + /** + * @description This is a description with a {@link ClassRef} reference + */ + public enum MyClass {} + `; + + const input2 = 'public class ClassRef {}'; + + const result = await generateDocs([ + unparsedApexBundleFromRawString(input1), + unparsedApexBundleFromRawString(input2), + ])(); + expect(result).documentationBundleHasLength(2); + assertEither(result, (data) => + expect(data).firstDocContains('This is a description with a [ClassRef](ClassRef.md) reference'), + ); + }); + + it('displays descriptions with emails', async () => { + const input = ` + /** + * @description This is a description with an {@email test@testerson.com} email + */ + public class MyClass {} + `; + + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => + expect(data).firstDocContains( + 'This is a description with an [test@testerson.com](mailto:test@testerson.com) email', + ), + ); + }); + + it('displays @sees with accurately resolved links', async () => { + const input1 = ` + /** + * @see ClassRef + */ + public class MyClass {} + `; + + const input2 = 'public class ClassRef {}'; + + const result = await generateDocs([ + unparsedApexBundleFromRawString(input1), + unparsedApexBundleFromRawString(input2), + ])(); + expect(result).documentationBundleHasLength(2); + assertEither(result, (data) => expect(data).firstDocContains('See')); + assertEither(result, (data) => expect(data).firstDocContains('[ClassRef](ClassRef.md)')); + }); + + it('displays @sees without links when the reference is not found', async () => { + const input = ` + /** + * @see ClassRef + */ + public class MyClass {} + `; + + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); + + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('See')); + assertEither(result, (data) => expect(data).firstDocContains('ClassRef')); + }); + + it('displays the namespace if present in the config', async () => { + const input = 'public class MyClass {}'; + + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { namespace: 'MyNamespace' })(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('## Namespace')); + assertEither(result, (data) => expect(data).firstDocContains('MyNamespace')); + }); + + it('does not display the namespace if not present in the config', async () => { + const input = 'public class MyClass {}'; + + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContainsNot('## Namespace')); + }); + + it('displays a mermaid diagram', async () => { + const input = ` + /** + * @mermaid + * \`\`\`mermaid + * graph TD + * A[Square Rect] -- Link text --> B((Circle)) + * A --> C(Round Rect) + * B --> D{Rhombus} + * C --> D + * \`\`\` + */ + public class MyClass {} + `; + + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('```mermaid')); + assertEither(result, (data) => expect(data).firstDocContains('graph TD')); + }); + + it('displays an example code block', async () => { + const input = ` + /** + * @example + * \`\`\`apex + * public class MyClass { + * public void myMethod() { + * System.debug('Hello, World!'); + * } + * } + * \`\`\` + */ + public class MyClass {}`; + + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); + + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('```apex')); + assertEither(result, (data) => expect(data).firstDocContains('public class MyClass')); + }); + + it('does not display tags marked as excluded', async () => { + const input = ` + /** + * @see ClassRef + */ + public class MyClass {} + `; + + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { + excludeTags: ['see'], + })(); + + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContainsNot('See')); + }); + }); +}); diff --git a/src/core/markdown/__test__/generating-class-docs.spec.ts b/src/core/markdown/__test__/generating-class-docs.spec.ts index 9c4ab2c5..56ad9a36 100644 --- a/src/core/markdown/__test__/generating-class-docs.spec.ts +++ b/src/core/markdown/__test__/generating-class-docs.spec.ts @@ -1,5 +1,5 @@ import { extendExpect } from './expect-extensions'; -import { apexBundleFromRawString, generateDocs } from './test-helpers'; +import { unparsedApexBundleFromRawString, generateDocs } from './test-helpers'; import { assertEither } from '../../test-helpers/assert-either'; describe('When generating documentation for a class', () => { @@ -15,7 +15,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('## Methods')); }); @@ -28,7 +28,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: true })(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { sortAlphabetically: true })(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => { const aMethodIndex = data.docs[0].content.indexOf('aMethod'); @@ -45,7 +45,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: false })(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { sortAlphabetically: false })(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => { const aMethodIndex = data.docs[0].content.indexOf('aMethod'); @@ -61,7 +61,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('## Properties')); }); @@ -74,7 +74,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: true })(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { sortAlphabetically: true })(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => { const aPropertyIndex = data.docs[0].content.indexOf('aProperty'); @@ -91,7 +91,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: false })(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { sortAlphabetically: false })(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => { const aPropertyIndex = data.docs[0].content.indexOf('aProperty'); @@ -107,7 +107,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('## Fields')); }); @@ -120,7 +120,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: true })(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { sortAlphabetically: true })(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => { const aFieldIndex = data.docs[0].content.indexOf('aField'); @@ -137,7 +137,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: false })(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { sortAlphabetically: false })(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => { const aFieldIndex = data.docs[0].content.indexOf('aField'); @@ -153,7 +153,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('## Constructors')); }); @@ -165,7 +165,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('## Classes')); }); @@ -178,7 +178,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: true })(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { sortAlphabetically: true })(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => { const aInnerClassIndex = data.docs[0].content.indexOf('AInnerClass'); @@ -195,7 +195,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: false })(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { sortAlphabetically: false })(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => { const aInnerClassIndex = data.docs[0].content.indexOf('AInnerClass'); @@ -211,7 +211,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('## Interfaces')); }); @@ -224,7 +224,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: true })(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { sortAlphabetically: true })(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => { const aInnerInterfaceIndex = data.docs[0].content.indexOf('AInnerInterface'); @@ -241,7 +241,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: false })(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { sortAlphabetically: false })(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => { const aInnerInterfaceIndex = data.docs[0].content.indexOf('AInnerInterface'); @@ -257,7 +257,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('## Enums')); }); @@ -270,7 +270,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: true })(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { sortAlphabetically: true })(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => { const aInnerEnumIndex = data.docs[0].content.indexOf('AInnerEnum'); @@ -287,7 +287,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: false })(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { sortAlphabetically: false })(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => { const aInnerEnumIndex = data.docs[0].content.indexOf('AInnerEnum'); @@ -313,7 +313,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('```mermaid')); assertEither(result, (data) => expect(data).firstDocContains('graph TD')); @@ -334,7 +334,7 @@ describe('When generating documentation for a class', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('```apex')); assertEither(result, (data) => expect(data).firstDocContains('public class MyClass')); @@ -351,7 +351,10 @@ describe('When generating documentation for a class', () => { public class AnotherClass extends MyClass {} `; - const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])(); + const result = await generateDocs([ + unparsedApexBundleFromRawString(input1), + unparsedApexBundleFromRawString(input2), + ])(); expect(result).documentationBundleHasLength(2); assertEither(result, (data) => expect(data.docs.find((doc) => doc.source.name === 'AnotherClass')?.content).toContain('Inherited'), diff --git a/src/core/markdown/__test__/generating-custom-object-docs.spec.ts b/src/core/markdown/__test__/generating-custom-object-docs.spec.ts new file mode 100644 index 00000000..f08d0499 --- /dev/null +++ b/src/core/markdown/__test__/generating-custom-object-docs.spec.ts @@ -0,0 +1,129 @@ +import { extendExpect } from './expect-extensions'; +import { + customField, + customObjectGenerator, + generateDocs, + unparsedFieldBundleFromRawString, + unparsedObjectBundleFromRawString, +} from './test-helpers'; +import { assertEither } from '../../test-helpers/assert-either'; + +describe('Generates Custom Object documentation', () => { + beforeAll(() => { + extendExpect(); + }); + + describe('documentation content', () => { + it('displays the object label as a heading', async () => { + const input = unparsedObjectBundleFromRawString({ + rawContent: customObjectGenerator(), + filePath: 'src/object/TestObject__c.object-meta.xml', + }); + + const result = await generateDocs([input])(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('# MyTestObject')); + }); + + it('displays the object description', async () => { + const input = unparsedObjectBundleFromRawString({ + rawContent: customObjectGenerator(), + filePath: 'src/object/TestObject__c.object-meta.xml', + }); + + const result = await generateDocs([input])(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('test object for testing')); + }); + + it('displays the object api name', async () => { + const input = unparsedObjectBundleFromRawString({ + rawContent: customObjectGenerator(), + filePath: 'src/object/TestObject__c.object-meta.xml', + }); + + const result = await generateDocs([input])(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('`TestObject__c`')); + }); + + it('displays the Fields heading if fields are present', async () => { + const customObjectBundle = unparsedObjectBundleFromRawString({ + rawContent: customObjectGenerator(), + filePath: 'src/object/TestObject__c.object-meta.xml', + }); + + const customFieldBundle = unparsedFieldBundleFromRawString({ + rawContent: customField, + filePath: 'src/object/TestField__c.field-meta.xml', + parentName: 'TestObject__c', + }); + + const result = await generateDocs([customObjectBundle, customFieldBundle])(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('## Fields')); + }); + + it('does not display the Fields heading if no fields are present', async () => { + const input = unparsedObjectBundleFromRawString({ + rawContent: customObjectGenerator(), + filePath: 'src/object/TestObject__c.object-meta.xml', + }); + + const result = await generateDocs([input])(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).not.firstDocContains('## Fields')); + }); + + it('displays the field label as a heading', async () => { + const customObjectBundle = unparsedObjectBundleFromRawString({ + rawContent: customObjectGenerator(), + filePath: 'src/object/TestObject__c.object-meta.xml', + }); + + const customFieldBundle = unparsedFieldBundleFromRawString({ + rawContent: customField, + filePath: 'src/object/TestField__c.field-meta.xml', + parentName: 'TestObject__c', + }); + + const result = await generateDocs([customObjectBundle, customFieldBundle])(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('## PhotoUrl')); + }); + + it('displays the field description', async () => { + const customObjectBundle = unparsedObjectBundleFromRawString({ + rawContent: customObjectGenerator(), + filePath: 'src/object/TestObject__c.object-meta.xml', + }); + + const customFieldBundle = unparsedFieldBundleFromRawString({ + rawContent: customField, + filePath: 'src/object/TestField__c.field-meta.xml', + parentName: 'TestObject__c', + }); + + const result = await generateDocs([customObjectBundle, customFieldBundle])(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('A URL that points to a photo')); + }); + + it('displays the field api name', async () => { + const customObjectBundle = unparsedObjectBundleFromRawString({ + rawContent: customObjectGenerator(), + filePath: 'src/object/TestObject__c.object-meta.xml', + }); + + const customFieldBundle = unparsedFieldBundleFromRawString({ + rawContent: customField, + filePath: 'src/object/TestField__c.field-meta.xml', + parentName: 'TestObject__c', + }); + + const result = await generateDocs([customObjectBundle, customFieldBundle])(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => expect(data).firstDocContains('`TestField__c`')); + }); + }); +}); diff --git a/src/core/markdown/__test__/generating-docs.spec.ts b/src/core/markdown/__test__/generating-docs.spec.ts index 769300f4..e8f6fb32 100644 --- a/src/core/markdown/__test__/generating-docs.spec.ts +++ b/src/core/markdown/__test__/generating-docs.spec.ts @@ -1,6 +1,11 @@ import { DocPageData, PostHookDocumentationBundle } from '../../shared/types'; import { extendExpect } from './expect-extensions'; -import { apexBundleFromRawString, generateDocs } from './test-helpers'; +import { + unparsedApexBundleFromRawString, + generateDocs, + unparsedObjectBundleFromRawString, + customObjectGenerator, +} from './test-helpers'; import { assertEither } from '../../test-helpers/assert-either'; function aSingleDoc(result: PostHookDocumentationBundle): DocPageData { @@ -14,7 +19,7 @@ describe('When generating documentation', () => { }); describe('the resulting files', () => { - it('are named after the type', async () => { + it('Apex code is named after the type', async () => { const properties: [string, string][] = [ ['public class MyClass {}', 'MyClass.md'], ['public interface MyInterface {}', 'MyInterface.md'], @@ -22,12 +27,12 @@ describe('When generating documentation', () => { ]; for (const [input, expected] of properties) { - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); assertEither(result, (data) => expect(aSingleDoc(data).outputDocPath).toContain(expected)); } }); - it('are placed in the miscellaneous folder if no group is provided', async () => { + it('Apex code is placed in the miscellaneous folder if no group is provided', async () => { const properties: [string, string][] = [ ['public class MyClass {}', 'miscellaneous'], ['public interface MyInterface {}', 'miscellaneous'], @@ -35,12 +40,42 @@ describe('When generating documentation', () => { ]; for (const [input, expected] of properties) { - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); assertEither(result, (data) => expect(aSingleDoc(data).outputDocPath).toContain(expected)); } }); - it('are placed in the slugified group folder if a group is provided', async () => { + it('SObject code is placed in the custom objects folder', async () => { + const properties: [ + { + rawContent: string; + filePath: string; + }, + string, + ][] = [ + [ + { + rawContent: customObjectGenerator(), + filePath: 'src/object/MyFirstObject__c.object-meta.xml', + }, + 'custom-objects', + ], + [ + { + rawContent: customObjectGenerator(), + filePath: 'src/object/MySecondObject__c.object-meta.xml', + }, + 'custom-objects', + ], + ]; + + for (const [input, expected] of properties) { + const result = await generateDocs([unparsedObjectBundleFromRawString(input)])(); + assertEither(result, (data) => expect(aSingleDoc(data).outputDocPath).toContain(expected)); + } + }); + + it('Apex code is placed in the slugified group folder if a group is provided', async () => { const properties: [string, string][] = [ [ `/** @@ -66,14 +101,14 @@ describe('When generating documentation', () => { ]; for (const [input, expected] of properties) { - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); assertEither(result, (data) => expect(aSingleDoc(data).outputDocPath).toContain(expected)); } }); }); describe('the generated bundles', () => { - it('return the type', async () => { + it('Apex code returns the type', async () => { const properties: [string, string][] = [ ['public class MyClass {}', 'class'], ['public interface MyInterface {}', 'interface'], @@ -81,21 +116,61 @@ describe('When generating documentation', () => { ]; for (const [input, expected] of properties) { - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); assertEither(result, (data) => expect(aSingleDoc(data).source.type).toBe(expected)); } }); - it('do not return files out of scope', async () => { + it('SObjects return the type', async () => { + const properties: [ + { + rawContent: string; + filePath: string; + }, + string, + ][] = [ + [ + { + rawContent: customObjectGenerator(), + filePath: 'src/object/MyFirstObject__c.object-meta.xml', + }, + 'customobject', + ], + ]; + + for (const [input, expected] of properties) { + const result = await generateDocs([unparsedObjectBundleFromRawString(input)])(); + assertEither(result, (data) => expect(aSingleDoc(data).source.type).toBe(expected)); + } + }); + + it('do not return Apex files out of scope', async () => { const input1 = 'global class MyClass {}'; const input2 = 'public class AnotherClass {}'; - const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)], { - scope: ['global'], - })(); + const result = await generateDocs( + [unparsedApexBundleFromRawString(input1), unparsedApexBundleFromRawString(input2)], + { + scope: ['global'], + }, + )(); expect(result).documentationBundleHasLength(1); }); + it('does not return non-deployed custom objects', async () => { + const input = customObjectGenerator({ deploymentStatus: 'InDevelopment', visibility: 'Public' }); + + const result = await generateDocs([unparsedObjectBundleFromRawString({ rawContent: input, filePath: 'test' })])(); + expect(result).documentationBundleHasLength(0); + }); + + it('does not return non-public custom objects', async () => { + const input = customObjectGenerator({ deploymentStatus: 'Deployed', visibility: 'Protected' }); + + const result = await generateDocs([unparsedObjectBundleFromRawString({ rawContent: input, filePath: 'test' })])(); + expect(result).documentationBundleHasLength(0); + }); + it('do not return files that have an @ignore in the docs', async () => { const input = ` /** @@ -103,13 +178,13 @@ describe('When generating documentation', () => { */ public class MyClass {}`; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(0); }); }); describe('the documentation content', () => { - it('includes a heading with the type name', async () => { + it('includes a heading with the type name for Apex code', async () => { const properties: [string, string][] = [ ['public class MyClass {}', 'MyClass Class'], ['public enum MyEnum {}', 'MyEnum Enum'], @@ -117,263 +192,19 @@ describe('When generating documentation', () => { ]; for (const [input, expected] of properties) { - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains(expected)); } }); - it('displays type level annotations', async () => { - const input = ` - @NamespaceAccessible - public class MyClass { - @Deprecated - public void myMethod() {} - } - `; - - const result = await generateDocs([apexBundleFromRawString(input)])(); - - expect(result).documentationBundleHasLength(1); - assertEither(result, (data) => expect(data).firstDocContains('NAMESPACEACCESSIBLE')); - assertEither(result, (data) => expect(data).firstDocContains('DEPRECATED')); - }); - - it('displays metadata as annotations', async () => { - const input = 'public class MyClass {}'; - const metadata = ` - - - 59.0 - Active - - `; - - const result = await generateDocs([apexBundleFromRawString(input, metadata)])(); - - expect(result).documentationBundleHasLength(1); - assertEither(result, (data) => expect(data).firstDocContains('APIVERSION')); - assertEither(result, (data) => expect(data).firstDocContains('STATUS')); - }); - - it('displays the description when no @description tag is used', async () => { - const input = ` - /** - * This is a description - */ - public class MyClass {} - `; - - const result = await generateDocs([apexBundleFromRawString(input)])(); - - expect(result).documentationBundleHasLength(1); - assertEither(result, (data) => expect(data).firstDocContains('This is a description')); - }); - - it('displays the description when a @description tag is used', async () => { - const input = ` - /** - * @description This is a description - */ - public class MyClass {}`; - - const result = await generateDocs([apexBundleFromRawString(input)])(); - expect(result).documentationBundleHasLength(1); - assertEither(result, (data) => expect(data).firstDocContains('This is a description')); - }); - - it('display custom documentation tags', async () => { - const input = ` - /** - * @custom-tag My Value - */ - public class MyClass {} - `; - - const result = await generateDocs([apexBundleFromRawString(input)])(); - expect(result).documentationBundleHasLength(1); - assertEither(result, (data) => expect(data).firstDocContains('Custom Tag')); - assertEither(result, (data) => expect(data).firstDocContains('My Value')); - }); - - it('displays the group', async () => { - const input = ` - /** - * @group MyGroup - */ - public class MyClass {}`; - - const result = await generateDocs([apexBundleFromRawString(input)])(); - expect(result).documentationBundleHasLength(1); - assertEither(result, (data) => expect(data).firstDocContains('Group')); - assertEither(result, (data) => expect(data).firstDocContains('MyGroup')); - }); - - it('displays the author', async () => { - const input = ` - /** - * @author John Doe - */ - public class MyClass {}`; - - const result = await generateDocs([apexBundleFromRawString(input)])(); - expect(result).documentationBundleHasLength(1); - assertEither(result, (data) => expect(data).firstDocContains('Author')); - assertEither(result, (data) => expect(data).firstDocContains('John Doe')); - }); - - it('displays the date', async () => { - const input = ` - /** - * @date 2021-01-01 - */ - public class MyClass {}`; - - const result = await generateDocs([apexBundleFromRawString(input)])(); - expect(result).documentationBundleHasLength(1); - assertEither(result, (data) => expect(data).firstDocContains('Date')); - assertEither(result, (data) => expect(data).firstDocContains('2021-01-01')); - }); - - it('displays descriptions with links', async () => { - const input1 = ` - /** - * @description This is a description with a {@link ClassRef} reference - */ - public enum MyClass {} - `; - - const input2 = 'public class ClassRef {}'; - - const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])(); - expect(result).documentationBundleHasLength(2); - assertEither(result, (data) => - expect(data).firstDocContains('This is a description with a [ClassRef](ClassRef.md) reference'), - ); - }); - - it('displays descriptions with emails', async () => { - const input = ` - /** - * @description This is a description with an {@email test@testerson.com} email - */ - public class MyClass {} - `; - - const result = await generateDocs([apexBundleFromRawString(input)])(); - expect(result).documentationBundleHasLength(1); - assertEither(result, (data) => - expect(data).firstDocContains( - 'This is a description with an [test@testerson.com](mailto:test@testerson.com) email', - ), - ); - }); - - it('displays @sees with accurately resolved links', async () => { - const input1 = ` - /** - * @see ClassRef - */ - public class MyClass {} - `; - - const input2 = 'public class ClassRef {}'; - - const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])(); - expect(result).documentationBundleHasLength(2); - assertEither(result, (data) => expect(data).firstDocContains('See')); - assertEither(result, (data) => expect(data).firstDocContains('[ClassRef](ClassRef.md)')); - }); - - it('displays @sees without links when the reference is not found', async () => { - const input = ` - /** - * @see ClassRef - */ - public class MyClass {} - `; - - const result = await generateDocs([apexBundleFromRawString(input)])(); - - expect(result).documentationBundleHasLength(1); - assertEither(result, (data) => expect(data).firstDocContains('See')); - assertEither(result, (data) => expect(data).firstDocContains('ClassRef')); - }); - - it('displays the namespace if present in the config', async () => { - const input = 'public class MyClass {}'; - - const result = await generateDocs([apexBundleFromRawString(input)], { namespace: 'MyNamespace' })(); - expect(result).documentationBundleHasLength(1); - assertEither(result, (data) => expect(data).firstDocContains('## Namespace')); - assertEither(result, (data) => expect(data).firstDocContains('MyNamespace')); - }); - - it('does not display the namespace if not present in the config', async () => { - const input = 'public class MyClass {}'; - - const result = await generateDocs([apexBundleFromRawString(input)])(); - expect(result).documentationBundleHasLength(1); - assertEither(result, (data) => expect(data).firstDocContainsNot('## Namespace')); - }); - - it('displays a mermaid diagram', async () => { - const input = ` - /** - * @mermaid - * \`\`\`mermaid - * graph TD - * A[Square Rect] -- Link text --> B((Circle)) - * A --> C(Round Rect) - * B --> D{Rhombus} - * C --> D - * \`\`\` - */ - public class MyClass {} - `; - - const result = await generateDocs([apexBundleFromRawString(input)])(); - expect(result).documentationBundleHasLength(1); - assertEither(result, (data) => expect(data).firstDocContains('```mermaid')); - assertEither(result, (data) => expect(data).firstDocContains('graph TD')); - }); - - it('displays an example code block', async () => { - const input = ` - /** - * @example - * \`\`\`apex - * public class MyClass { - * public void myMethod() { - * System.debug('Hello, World!'); - * } - * } - * \`\`\` - */ - public class MyClass {}`; - - const result = await generateDocs([apexBundleFromRawString(input)])(); - - expect(result).documentationBundleHasLength(1); - assertEither(result, (data) => expect(data).firstDocContains('```apex')); - assertEither(result, (data) => expect(data).firstDocContains('public class MyClass')); - }); - - it('does not display tags marked as excluded', async () => { - const input = ` - /** - * @see ClassRef - */ - public class MyClass {} - `; - - const result = await generateDocs([apexBundleFromRawString(input)], { - excludeTags: ['see'], - })(); + it('includes a heading with the Custom Object label', async () => { + const input = customObjectGenerator(); + const result = await generateDocs([unparsedObjectBundleFromRawString({ rawContent: input, filePath: 'test' })])(); expect(result).documentationBundleHasLength(1); - assertEither(result, (data) => expect(data).firstDocContainsNot('See')); + assertEither(result, (data) => expect(data).firstDocContains('MyTestObject')); }); }); }); diff --git a/src/core/markdown/__test__/generating-enum-docs.spec.ts b/src/core/markdown/__test__/generating-enum-docs.spec.ts index f0f7aa67..66bf94bb 100644 --- a/src/core/markdown/__test__/generating-enum-docs.spec.ts +++ b/src/core/markdown/__test__/generating-enum-docs.spec.ts @@ -1,5 +1,5 @@ import { extendExpect } from './expect-extensions'; -import { apexBundleFromRawString, generateDocs } from './test-helpers'; +import { unparsedApexBundleFromRawString, generateDocs } from './test-helpers'; import { assertEither } from '../../test-helpers/assert-either'; describe('Generates enum documentation', () => { @@ -16,7 +16,7 @@ describe('Generates enum documentation', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('## Values')); assertEither(result, (data) => expect(data).firstDocContains('VALUE1')); @@ -31,7 +31,7 @@ describe('Generates enum documentation', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: true })(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { sortAlphabetically: true })(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('## Values')); assertEither(result, (data) => { @@ -49,7 +49,7 @@ describe('Generates enum documentation', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: false })(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { sortAlphabetically: false })(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('## Values')); assertEither(result, (data) => { diff --git a/src/core/markdown/__test__/generating-interface-docs.spec.ts b/src/core/markdown/__test__/generating-interface-docs.spec.ts index c6d7e964..21afaf9c 100644 --- a/src/core/markdown/__test__/generating-interface-docs.spec.ts +++ b/src/core/markdown/__test__/generating-interface-docs.spec.ts @@ -1,5 +1,5 @@ import { extendExpect } from './expect-extensions'; -import { apexBundleFromRawString, generateDocs } from './test-helpers'; +import { unparsedApexBundleFromRawString, generateDocs } from './test-helpers'; import { assertEither } from '../../test-helpers/assert-either'; describe('Generates interface documentation', () => { @@ -16,7 +16,7 @@ describe('Generates interface documentation', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('## Methods')); }); @@ -29,7 +29,7 @@ describe('Generates interface documentation', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: true })(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { sortAlphabetically: true })(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => { expect(data.docs[0].content.indexOf('anotherMethod')).toBeLessThan(data.docs[0].content.indexOf('myMethod')); @@ -44,7 +44,7 @@ describe('Generates interface documentation', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: false })(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)], { sortAlphabetically: false })(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => { expect(data.docs[0].content.indexOf('myMethod')).toBeLessThan(data.docs[0].content.indexOf('anotherMethod')); @@ -68,7 +68,7 @@ describe('Generates interface documentation', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('```mermaid')); assertEither(result, (data) => expect(data).firstDocContains('graph TD')); @@ -89,7 +89,7 @@ describe('Generates interface documentation', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('```apex')); assertEither(result, (data) => expect(data).firstDocContains('public class MyClass')); @@ -102,7 +102,7 @@ describe('Generates interface documentation', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('### Signature')); }); @@ -114,7 +114,7 @@ describe('Generates interface documentation', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('### Parameters')); }); @@ -126,7 +126,7 @@ describe('Generates interface documentation', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('### Return Type')); }); @@ -141,7 +141,7 @@ describe('Generates interface documentation', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect(data).firstDocContains('### Throws')); }); @@ -157,7 +157,10 @@ describe('Generates interface documentation', () => { public interface AnotherInterface extends MyInterface {} `; - const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])(); + const result = await generateDocs([ + unparsedApexBundleFromRawString(input1), + unparsedApexBundleFromRawString(input2), + ])(); expect(result).documentationBundleHasLength(2); assertEither(result, (data) => expect(data.docs.find((doc) => doc.outputDocPath.includes('AnotherInterface'))?.content).toContain( diff --git a/src/core/markdown/__test__/generating-reference-guide.spec.ts b/src/core/markdown/__test__/generating-reference-guide.spec.ts index ad64980c..510e9458 100644 --- a/src/core/markdown/__test__/generating-reference-guide.spec.ts +++ b/src/core/markdown/__test__/generating-reference-guide.spec.ts @@ -1,7 +1,12 @@ import { extendExpect } from './expect-extensions'; import { pipe } from 'fp-ts/function'; import * as E from 'fp-ts/Either'; -import { apexBundleFromRawString, generateDocs } from './test-helpers'; +import { + unparsedApexBundleFromRawString, + generateDocs, + customObjectGenerator, + unparsedObjectBundleFromRawString, +} from './test-helpers'; import { ReferenceGuidePageData } from '../../shared/types'; import { assertEither } from '../../test-helpers/assert-either'; @@ -38,8 +43,17 @@ describe('When generating the Reference Guide', () => { public class MyClass {} `; - const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])(); - expect(result).documentationBundleHasLength(2); + const input3 = { + rawContent: customObjectGenerator(), + filePath: 'src/object/TestObject__c.object-meta.xml', + }; + + const result = await generateDocs([ + unparsedApexBundleFromRawString(input1), + unparsedApexBundleFromRawString(input2), + unparsedObjectBundleFromRawString(input3), + ])(); + expect(result).documentationBundleHasLength(3); assertEither(result, (data) => expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('[MyEnum](miscellaneous/MyEnum.md)'), @@ -47,9 +61,14 @@ describe('When generating the Reference Guide', () => { assertEither(result, (data) => expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('[MyClass](miscellaneous/MyClass.md)'), ); + assertEither(result, (data) => + expect((data.referenceGuide as ReferenceGuidePageData).content).toContain( + '[TestObject__c](custom-objects/TestObject__c.md)', + ), + ); }); - it('groups things under Miscellaneous if no group is provided', async () => { + it('groups Apex code under Miscellaneous if no group is provided', async () => { const input = ` public enum MyEnum { VALUE1, @@ -57,14 +76,14 @@ describe('When generating the Reference Guide', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('## Miscellaneous'), ); }); - it('group things under the provided group', async () => { + it('group Apex code under the provided group', async () => { const input = ` /** * @group MyGroup @@ -75,13 +94,41 @@ describe('When generating the Reference Guide', () => { } `; - const result = await generateDocs([apexBundleFromRawString(input)])(); + const result = await generateDocs([unparsedApexBundleFromRawString(input)])(); expect(result).documentationBundleHasLength(1); assertEither(result, (data) => expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('## MyGroup'), ); }); + it('group SObjects under the Custom Objects group by default', async () => { + const input = { + rawContent: customObjectGenerator(), + filePath: 'src/object/TestObject__c.object-meta.xml', + }; + + const result = await generateDocs([unparsedObjectBundleFromRawString(input)])(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => + expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('## Custom Objects'), + ); + }); + + it('groups SObjects under the provided group', async () => { + const input = { + rawContent: customObjectGenerator(), + filePath: 'src/object/TestObject__c.object-meta.xml', + }; + + const result = await generateDocs([unparsedObjectBundleFromRawString(input)], { + customObjectsGroupName: 'MyCustomObjects', + })(); + expect(result).documentationBundleHasLength(1); + assertEither(result, (data) => + expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('## MyCustomObjects'), + ); + }); + it('displays groups in alphabetical order', async () => { const input1 = ` /** @@ -100,7 +147,10 @@ describe('When generating the Reference Guide', () => { public class MyClass {} `; - const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])(); + const result = await generateDocs([ + unparsedApexBundleFromRawString(input1), + unparsedApexBundleFromRawString(input2), + ])(); expect(result).documentationBundleHasLength(2); pipe( result, @@ -133,14 +183,27 @@ describe('When generating the Reference Guide', () => { public class MyClass {} `; - const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])(); - expect(result).documentationBundleHasLength(2); + const input3 = { + rawContent: customObjectGenerator(), + filePath: 'src/object/TestObject__c.object-meta.xml', + }; + + const result = await generateDocs([ + unparsedApexBundleFromRawString(input1), + unparsedApexBundleFromRawString(input2), + unparsedObjectBundleFromRawString(input3), + ])(); + + expect(result).documentationBundleHasLength(3); assertEither(result, (data) => expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('## Group1'), ); assertEither(result, (data) => expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('MyClass'), ); + assertEither(result, (data) => + expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('TestObject__c'), + ); assertEither(result, (data) => expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('MyEnum')); }); @@ -162,11 +225,24 @@ describe('When generating the Reference Guide', () => { public class MyClass {} `; - const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])(); - expect(result).documentationBundleHasLength(2); + const input3 = { + rawContent: customObjectGenerator(), + filePath: 'src/object/ATestObject__c.object-meta.xml', + }; + + const result = await generateDocs([ + unparsedApexBundleFromRawString(input1), + unparsedApexBundleFromRawString(input2), + unparsedObjectBundleFromRawString(input3), + ])(); + + expect(result).documentationBundleHasLength(3); assertEither(result, (data) => expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('This is a description'), ); + assertEither(result, (data) => + expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('test object for testing'), + ); }); it('returns a reference guide with descriptions with links to all other files', async () => { @@ -188,7 +264,10 @@ describe('When generating the Reference Guide', () => { public class MyClass {} `; - const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])(); + const result = await generateDocs([ + unparsedApexBundleFromRawString(input1), + unparsedApexBundleFromRawString(input2), + ])(); expect(result).documentationBundleHasLength(2); assertEither(result, (data) => expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('with a [MyClass](group2/MyClass.md)'), diff --git a/src/core/markdown/__test__/inheritance-chain.test.ts b/src/core/markdown/__test__/inheritance-chain.test.ts index 4b97fdaa..f6c76e59 100644 --- a/src/core/markdown/__test__/inheritance-chain.test.ts +++ b/src/core/markdown/__test__/inheritance-chain.test.ts @@ -1,5 +1,5 @@ import { ClassMirrorBuilder } from '../../../test-helpers/ClassMirrorBuilder'; -import { createInheritanceChain } from '../../reflection/inheritance-chain'; +import { createInheritanceChain } from '../../reflection/apex/inheritance-chain'; describe('inheritance chain for classes', () => { test('returns an empty list of the class does not extend any other class', () => { diff --git a/src/core/markdown/__test__/test-helpers.ts b/src/core/markdown/__test__/test-helpers.ts index a3cd95a8..d884958f 100644 --- a/src/core/markdown/__test__/test-helpers.ts +++ b/src/core/markdown/__test__/test-helpers.ts @@ -1,20 +1,54 @@ -import { UnparsedSourceFile } from '../../shared/types'; +import { + UnparsedApexBundle, + UnparsedCustomFieldBundle, + UnparsedCustomObjectBundle, + UnparsedSourceBundle, +} from '../../shared/types'; import { generateDocs as gen, MarkdownGeneratorConfig } from '../generate-docs'; import { referenceGuideTemplate } from '../templates/reference-guide'; -export function apexBundleFromRawString(raw: string, rawMetadata?: string): UnparsedSourceFile { +export function unparsedApexBundleFromRawString(raw: string, rawMetadata?: string): UnparsedApexBundle { return { + type: 'apex', + name: 'Test', filePath: 'test.cls', content: raw, metadataContent: rawMetadata ?? null, }; } -export function generateDocs(apexBundles: UnparsedSourceFile[], config?: Partial) { +export function unparsedObjectBundleFromRawString(meta: { + rawContent: string; + filePath: string; +}): UnparsedCustomObjectBundle { + return { + type: 'customobject', + name: 'TestObject__c', + filePath: meta.filePath, + content: meta.rawContent, + }; +} + +export function unparsedFieldBundleFromRawString(meta: { + rawContent: string; + filePath: string; + parentName: string; +}): UnparsedCustomFieldBundle { + return { + type: 'customfield', + name: 'TestField__c', + filePath: meta.filePath, + content: meta.rawContent, + parentName: meta.parentName, + }; +} + +export function generateDocs(apexBundles: UnparsedSourceBundle[], config?: Partial) { return gen(apexBundles, { targetDir: 'target', scope: ['global', 'public'], defaultGroupName: 'Miscellaneous', + customObjectsGroupName: 'Custom Objects', sortAlphabetically: false, referenceGuideTemplate: referenceGuideTemplate, linkingStrategy: 'relative', @@ -24,3 +58,29 @@ export function generateDocs(apexBundles: UnparsedSourceFile[], config?: Partial ...config, }); } + +export function customObjectGenerator( + config: { deploymentStatus: string; visibility: string } = { deploymentStatus: 'Deployed', visibility: 'Public' }, +) { + return ` + + + ${config.deploymentStatus} + test object for testing + + MyFirstObjects + ${config.visibility} + `; +} + +export const customField = ` + + + PhotoUrl__c + false + + false + false + Url + A URL that points to a photo +`; diff --git a/src/core/markdown/adapters/__tests__/interface-adapter.spec.ts b/src/core/markdown/adapters/__tests__/interface-adapter.spec.ts index 6b818cea..b69fdd7b 100644 --- a/src/core/markdown/adapters/__tests__/interface-adapter.spec.ts +++ b/src/core/markdown/adapters/__tests__/interface-adapter.spec.ts @@ -1,4 +1,4 @@ -import { typeToRenderable } from '../apex-types'; +import { typeToRenderable } from '../type-to-renderable'; import { InterfaceMirrorBuilder } from '../../../../test-helpers/InterfaceMirrorBuilder'; import { AnnotationBuilder } from '../../../../test-helpers/AnnotationBuilder'; import { MethodMirrorBuilder, ParameterBuilder } from '../../../../test-helpers/MethodMirrorBuilder'; @@ -13,6 +13,7 @@ const defaultMarkdownGeneratorConfig: MarkdownGeneratorConfig = { scope: ['global', 'public'], namespace: '', defaultGroupName: 'Miscellaneous', + customObjectsGroupName: 'Custom Objects', referenceGuideTemplate: '', sortAlphabetically: false, linkingStrategy: 'relative', diff --git a/src/core/markdown/adapters/fields-and-properties.ts b/src/core/markdown/adapters/fields-and-properties.ts index f8450ce1..500fe5cc 100644 --- a/src/core/markdown/adapters/fields-and-properties.ts +++ b/src/core/markdown/adapters/fields-and-properties.ts @@ -2,7 +2,7 @@ import { CodeBlock, FieldMirrorWithInheritance, PropertyMirrorWithInheritance, - RenderableField, + RenderableApexField, GetRenderableContentByTypeName, } from '../../renderables/types'; import { adaptDocumentable } from '../../renderables/documentables'; @@ -11,7 +11,7 @@ export function adaptFieldOrProperty( field: FieldMirrorWithInheritance | PropertyMirrorWithInheritance, linkGenerator: GetRenderableContentByTypeName, baseHeadingLevel: number, -): RenderableField { +): RenderableApexField { function buildSignature(): CodeBlock { const { access_modifier, name } = field; const memberModifiers = field.memberModifiers.join(' '); diff --git a/src/core/markdown/adapters/reference-guide.ts b/src/core/markdown/adapters/reference-guide.ts index 5e645cd7..81804530 100644 --- a/src/core/markdown/adapters/reference-guide.ts +++ b/src/core/markdown/adapters/reference-guide.ts @@ -1,32 +1,32 @@ import { MarkdownGeneratorConfig } from '../generate-docs'; import { DocPageReference, ParsedFile } from '../../shared/types'; +import { getTypeGroup } from '../../shared/utils'; +import { ObjectMetadata } from '../../reflection/sobject/reflect-custom-object-sources'; import { Type } from '@cparra/apex-reflection'; export function parsedFilesToReferenceGuide( config: MarkdownGeneratorConfig, - parsedFiles: ParsedFile[], + parsedFiles: ParsedFile[], ): Record { return parsedFiles.reduce>((acc, parsedFile) => { - acc[parsedFile.type.name] = parsedFileToDocPageReference(config, parsedFile); + acc[parsedFile.source.name] = parsedFileToDocPageReference(config, parsedFile); return acc; }, {}); } -function parsedFileToDocPageReference(config: MarkdownGeneratorConfig, parsedFile: ParsedFile): DocPageReference { - const path = `${slugify(getTypeGroup(parsedFile.type, config))}/${parsedFile.type.name}.md`; +function parsedFileToDocPageReference( + config: MarkdownGeneratorConfig, + parsedFile: ParsedFile, +): DocPageReference { + const path = `${slugify(getTypeGroup(parsedFile.type, config))}/${parsedFile.source.name}.md`; return { source: parsedFile.source, - displayName: parsedFile.type.name, + displayName: parsedFile.source.name, outputDocPath: path, referencePath: path, }; } -function getTypeGroup(type: Type, config: MarkdownGeneratorConfig): string { - const groupAnnotation = type.docComment?.annotations.find((annotation) => annotation.name.toLowerCase() === 'group'); - return groupAnnotation?.body ?? config.defaultGroupName; -} - function slugify(text: string): string { return text .toLowerCase() diff --git a/src/core/markdown/adapters/renderable-bundle.ts b/src/core/markdown/adapters/renderable-bundle.ts index 2b0e580d..af4cd633 100644 --- a/src/core/markdown/adapters/renderable-bundle.ts +++ b/src/core/markdown/adapters/renderable-bundle.ts @@ -1,27 +1,37 @@ import { DocPageReference, ParsedFile } from '../../shared/types'; -import { Link, ReferenceGuideReference, Renderable, RenderableBundle } from '../../renderables/types'; -import { typeToRenderable } from './apex-types'; +import { + Link, + ReferenceGuideReference, + Renderable, + RenderableBundle, + RenderableContent, +} from '../../renderables/types'; +import { typeToRenderable } from './type-to-renderable'; import { adaptDescribable } from '../../renderables/documentables'; import { MarkdownGeneratorConfig } from '../generate-docs'; import { apply } from '#utils/fp'; -import { Type } from '@cparra/apex-reflection'; import { generateLink } from './generate-link'; +import { getTypeGroup } from '../../shared/utils'; +import { Type } from '@cparra/apex-reflection'; +import { ObjectMetadata } from '../../reflection/sobject/reflect-custom-object-sources'; export function parsedFilesToRenderableBundle( config: MarkdownGeneratorConfig, - parsedFiles: ParsedFile[], + parsedFiles: ParsedFile[], references: Record, ): RenderableBundle { const referenceFinder = apply(generateLink(config.linkingStrategy), references); - function toReferenceGuide(parsedFiles: ParsedFile[]): Record { + function toReferenceGuide( + parsedFiles: ParsedFile[], + ): Record { return parsedFiles.reduce>( addToReferenceGuide(apply(referenceFinder, '__base__'), config, references), {}, ); } - function toRenderables(parsedFiles: ParsedFile[]): Renderable[] { + function toRenderables(parsedFiles: ParsedFile[]): Renderable[] { return parsedFiles.reduce((acc, parsedFile) => { const renderable = typeToRenderable(parsedFile, apply(referenceFinder, parsedFile.source.name), config); acc.push(renderable); @@ -40,22 +50,29 @@ function addToReferenceGuide( config: MarkdownGeneratorConfig, references: Record, ) { - return (acc: Record, parsedFile: ParsedFile) => { + return (acc: Record, parsedFile: ParsedFile) => { const group: string = getTypeGroup(parsedFile.type, config); if (!acc[group]) { acc[group] = []; } acc[group].push({ - reference: references[parsedFile.type.name], - title: findLinkFromHome(parsedFile.type.name) as Link, - description: adaptDescribable(parsedFile.type.docComment?.descriptionLines, findLinkFromHome).description ?? null, + reference: references[parsedFile.source.name], + title: findLinkFromHome(parsedFile.source.name) as Link, + description: getRenderableDescription(parsedFile.type, findLinkFromHome), }); return acc; }; } -function getTypeGroup(type: Type, config: MarkdownGeneratorConfig): string { - const groupAnnotation = type.docComment?.annotations.find((annotation) => annotation.name.toLowerCase() === 'group'); - return groupAnnotation?.body ?? config.defaultGroupName; +function getRenderableDescription( + type: Type | ObjectMetadata, + findLinkFromHome: (referenceName: string) => string | Link, +): RenderableContent[] | null { + switch (type.type_name) { + case 'customobject': + return type.description ? [type.description] : null; + default: + return adaptDescribable(type.docComment?.descriptionLines, findLinkFromHome).description ?? null; + } } diff --git a/src/core/markdown/adapters/renderable-to-page-data.ts b/src/core/markdown/adapters/renderable-to-page-data.ts index cc291602..f5d84776 100644 --- a/src/core/markdown/adapters/renderable-to-page-data.ts +++ b/src/core/markdown/adapters/renderable-to-page-data.ts @@ -6,6 +6,7 @@ import { enumMarkdownTemplate } from '../templates/enum-template'; import { interfaceMarkdownTemplate } from '../templates/interface-template'; import { classMarkdownTemplate } from '../templates/class-template'; import { markdownDefaults } from '../../../defaults'; +import { customObjectTemplate } from '../templates/custom-object-template'; export const convertToDocumentationBundle = ( referenceGuideTitle: string, @@ -62,6 +63,7 @@ function renderableToPageData(referenceGuideReference: ReferenceGuideReference[] frontmatter: null, content: docContents, group: renderable.doc.group ?? markdownDefaults.defaultGroupName, + type: renderable.type, }; } @@ -77,6 +79,8 @@ function resolveApexTypeTemplate(renderable: Renderable): CompilationRequest { return interfaceMarkdownTemplate; case 'class': return classMarkdownTemplate; + case 'customobject': + return customObjectTemplate; } } diff --git a/src/core/markdown/adapters/apex-types.ts b/src/core/markdown/adapters/type-to-renderable.ts similarity index 77% rename from src/core/markdown/adapters/apex-types.ts rename to src/core/markdown/adapters/type-to-renderable.ts index 35708c37..cd66d8ec 100644 --- a/src/core/markdown/adapters/apex-types.ts +++ b/src/core/markdown/adapters/type-to-renderable.ts @@ -10,33 +10,42 @@ import { FieldMirrorWithInheritance, PropertyMirrorWithInheritance, GetRenderableContentByTypeName, + RenderableCustomObject, + RenderableCustomField, } from '../../renderables/types'; import { adaptDescribable, adaptDocumentable } from '../../renderables/documentables'; import { adaptConstructor, adaptMethod } from './methods-and-constructors'; import { adaptFieldOrProperty } from './fields-and-properties'; import { MarkdownGeneratorConfig } from '../generate-docs'; import { SourceFileMetadata } from '../../shared/types'; +import { ObjectMetadata } from '../../reflection/sobject/reflect-custom-object-sources'; +import { getTypeGroup } from '../../shared/utils'; +import { CustomFieldMetadata } from '../../reflection/sobject/reflect-custom-field-source'; -type GetReturnRenderable = T extends InterfaceMirror +type GetReturnRenderable = T extends InterfaceMirror ? RenderableInterface : T extends ClassMirror ? RenderableClass - : RenderableEnum; + : T extends EnumMirror + ? RenderableEnum + : RenderableCustomObject; -export function typeToRenderable( +export function typeToRenderable( parsedFile: { source: SourceFileMetadata; type: T }, linkGenerator: GetRenderableContentByTypeName, config: MarkdownGeneratorConfig, ): GetReturnRenderable & { filePath: string; namespace?: string } { - function getRenderable(): RenderableInterface | RenderableClass | RenderableEnum { + function getRenderable(): RenderableInterface | RenderableClass | RenderableEnum | RenderableCustomObject { const { type } = parsedFile; switch (type.type_name) { case 'enum': - return enumTypeToEnumSource(type as EnumMirror, linkGenerator) as RenderableEnum; + return enumTypeToEnumSource(type as EnumMirror, linkGenerator); case 'interface': - return interfaceTypeToInterfaceSource(type as InterfaceMirror, linkGenerator) as RenderableInterface; + return interfaceTypeToInterfaceSource(type as InterfaceMirror, linkGenerator); case 'class': - return classTypeToClassSource(type as ClassMirrorWithInheritanceChain, linkGenerator) as RenderableClass; + return classTypeToClassSource(type as ClassMirrorWithInheritanceChain, linkGenerator); + case 'customobject': + return objectMetadataToRenderable(type as ObjectMetadata, config); } } @@ -236,3 +245,55 @@ function singleGroup( value: toFlat(members, adapter, linkGenerator, headingLevel + 1), }; } + +function objectMetadataToRenderable( + objectMetadata: ObjectMetadata, + config: MarkdownGeneratorConfig, +): RenderableCustomObject { + return { + type: 'customobject', + headingLevel: 1, + apiName: getApiName(objectMetadata.name, config), + heading: objectMetadata.label, + name: objectMetadata.name, + doc: { + description: objectMetadata.description ? [objectMetadata.description] : [], + group: getTypeGroup(objectMetadata, config), + }, + hasFields: objectMetadata.fields.length > 0, + fields: { + headingLevel: 2, + heading: 'Fields', + value: objectMetadata.fields.map((field) => fieldMetadataToRenderable(field.type, config, 3)), + }, + }; +} + +function fieldMetadataToRenderable( + field: CustomFieldMetadata, + config: MarkdownGeneratorConfig, + headingLevel: number, +): RenderableCustomField { + return { + type: 'field', + headingLevel: headingLevel, + heading: field.label, + description: field.description ? [field.description] : [], + apiName: getApiName(field.name, config), + fieldType: field.type, + }; +} + +function getApiName(currentName: string, config: MarkdownGeneratorConfig) { + if (config.namespace) { + // first remove any `__c` suffix + const name = currentName.replace(/__c$/, ''); + // if the name still has an __, it's already namespaced + if (name.includes('__')) { + return name; + } + + return `${config.namespace}__${currentName}`; + } + return currentName; +} diff --git a/src/core/markdown/generate-docs.ts b/src/core/markdown/generate-docs.ts index 6bb0d92d..71a53ec9 100644 --- a/src/core/markdown/generate-docs.ts +++ b/src/core/markdown/generate-docs.ts @@ -9,7 +9,7 @@ import { Frontmatter, PostHookDocumentationBundle, ReferenceGuidePageData, - UnparsedSourceFile, + UnparsedApexBundle, TransformDocPage, TransformDocs, TransformReferenceGuide, @@ -17,20 +17,26 @@ import { DocPageReference, TransformReference, ParsedFile, + UnparsedCustomObjectBundle, + UnparsedSourceBundle, + UnparsedCustomFieldBundle, } from '../shared/types'; import { parsedFilesToRenderableBundle } from './adapters/renderable-bundle'; -import { reflectBundles } from '../reflection/reflect-source'; -import { addInheritanceChainToTypes } from '../reflection/inheritance-chain-expanion'; -import { addInheritedMembersToTypes } from '../reflection/inherited-member-expansion'; +import { reflectApexSource } from '../reflection/apex/reflect-apex-source'; +import { addInheritanceChainToTypes } from '../reflection/apex/inheritance-chain-expanion'; +import { addInheritedMembersToTypes } from '../reflection/apex/inherited-member-expansion'; import { convertToDocumentationBundle } from './adapters/renderable-to-page-data'; -import { filterScope } from '../reflection/filter-scope'; +import { filterScope } from '../reflection/apex/filter-scope'; import { Template } from '../template'; import { hookableTemplate } from './templates/hookable'; import { sortTypesAndMembers } from '../reflection/sort-types-and-members'; import { isSkip } from '../shared/utils'; import { parsedFilesToReferenceGuide } from './adapters/reference-guide'; -import { removeExcludedTags } from '../reflection/remove-excluded-tags'; -import { HookError } from '../errors/errors'; +import { removeExcludedTags } from '../reflection/apex/remove-excluded-tags'; +import { HookError, ReflectionErrors } from '../errors/errors'; +import { ObjectMetadata, reflectCustomObjectSources } from '../reflection/sobject/reflect-custom-object-sources'; +import { CustomFieldMetadata, reflectCustomFieldSources } from '../reflection/sobject/reflect-custom-field-source'; +import { Type } from '@cparra/apex-reflection'; export type MarkdownGeneratorConfig = Omit< UserDefinedMarkdownConfig, @@ -39,8 +45,7 @@ export type MarkdownGeneratorConfig = Omit< referenceGuideTemplate: string; }; -export function generateDocs(apexBundles: UnparsedSourceFile[], config: MarkdownGeneratorConfig) { - const filterOutOfScope = apply(filterScope, config.scope); +export function generateDocs(unparsedApexFiles: UnparsedSourceBundle[], config: MarkdownGeneratorConfig) { const convertToReferences = apply(parsedFilesToReferenceGuide, config); const convertToRenderableBundle = apply(parsedFilesToRenderableBundle, config); const convertToDocumentationBundleForTemplate = apply( @@ -49,23 +54,107 @@ export function generateDocs(apexBundles: UnparsedSourceFile[], config: Markdown config.referenceGuideTemplate, ); const sort = apply(sortTypesAndMembers, config.sortAlphabetically); + + function filterApexSourceFiles(sourceFiles: UnparsedSourceBundle[]): UnparsedApexBundle[] { + return sourceFiles.filter((sourceFile): sourceFile is UnparsedApexBundle => sourceFile.type === 'apex'); + } + + function filterCustomObjectsAndFields( + sourceFiles: UnparsedSourceBundle[], + ): (UnparsedCustomObjectBundle | UnparsedCustomFieldBundle)[] { + return sourceFiles.filter( + (sourceFile): sourceFile is UnparsedCustomObjectBundle => + sourceFile.type === 'customobject' || sourceFile.type === 'customfield', + ); + } + + function filterOutCustomFields(parsedFiles: ParsedFile[]): ParsedFile[] { + return parsedFiles.filter( + (parsedFile): parsedFile is ParsedFile => parsedFile.source.type !== 'customfield', + ); + } + + return pipe( + generateForApex(filterApexSourceFiles(unparsedApexFiles), config), + TE.chain((parsedApexFiles) => { + return pipe( + generateForObject(filterCustomObjectsAndFields(unparsedApexFiles)), + TE.map((parsedObjectFiles) => [...parsedApexFiles, ...parsedObjectFiles]), + ); + }), + TE.map((parsedFiles) => sort(filterOutCustomFields(parsedFiles))), + TE.bindTo('parsedFiles'), + TE.bind('references', ({ parsedFiles }) => + TE.right( + // Custom fields should not show up in the reference guide + convertToReferences(parsedFiles), + ), + ), + TE.flatMap(({ parsedFiles, references }) => transformReferenceHook(config)({ references, parsedFiles })), + TE.map(({ parsedFiles, references }) => convertToRenderableBundle(filterOutCustomFields(parsedFiles), references)), + TE.map(convertToDocumentationBundleForTemplate), + TE.flatMap(transformDocumentationBundleHook(config)), + TE.map(postHookCompile), + ); +} + +function generateForApex(apexBundles: UnparsedApexBundle[], config: MarkdownGeneratorConfig) { + const filterOutOfScope = apply(filterScope, config.scope); const removeExcluded = apply(removeExcludedTags, config.excludeTags); return pipe( apexBundles, - reflectBundles, + reflectApexSource, TE.map(filterOutOfScope), TE.map(addInheritedMembersToTypes), TE.map(addInheritanceChainToTypes), - TE.map(sort), TE.map(removeExcluded), - TE.bindTo('parsedFiles'), - TE.bind('references', ({ parsedFiles }) => TE.right(convertToReferences(parsedFiles))), - TE.flatMap(({ parsedFiles, references }) => transformReferenceHook(config)({ references, parsedFiles })), - TE.map(({ parsedFiles, references }) => convertToRenderableBundle(parsedFiles, references)), - TE.map(convertToDocumentationBundleForTemplate), - TE.flatMap(transformDocumentationBundleHook(config)), - TE.map(postHookCompile), + ); +} + +function generateForObject(objectBundles: (UnparsedCustomObjectBundle | UnparsedCustomFieldBundle)[]) { + function filterNonPublished(parsedFiles: ParsedFile[]): ParsedFile[] { + return parsedFiles.filter((parsedFile) => parsedFile.type.deploymentStatus === 'Deployed'); + } + + function filterNonPublic(parsedFiles: ParsedFile[]): ParsedFile[] { + return parsedFiles.filter((parsedFile) => parsedFile.type.visibility === 'Public'); + } + + const customObjects = objectBundles.filter( + (object): object is UnparsedCustomObjectBundle => object.type === 'customobject', + ); + + const customFields = objectBundles.filter( + (object): object is UnparsedCustomFieldBundle => object.type === 'customfield', + ); + + function generateForFields( + fields: UnparsedCustomFieldBundle[], + ): TE.TaskEither[]> { + return pipe(fields, reflectCustomFieldSources); + } + + return pipe( + customObjects, + reflectCustomObjectSources, + TE.map(filterNonPublished), + TE.map(filterNonPublic), + TE.bindTo('objects'), + TE.bind('fields', () => generateForFields(customFields)), + // Locate the fields for each object by using the parentName property + TE.map(({ objects, fields }) => { + return objects.map((object) => { + const objectFields = fields.filter((field) => field.type.parentName === object.type.name); + return { + ...object, + type: { + ...object.type, + fields: objectFields, + }, + } as ParsedFile; + }); + }), ); } diff --git a/src/core/markdown/templates/custom-object-template.ts b/src/core/markdown/templates/custom-object-template.ts new file mode 100644 index 00000000..ff0af90b --- /dev/null +++ b/src/core/markdown/templates/custom-object-template.ts @@ -0,0 +1,30 @@ +export const customObjectTemplate = ` +{{ heading headingLevel heading }} + +{{{renderContent doc.description}}} + +## API Name +\`{{apiName}}\` + +{{#if hasFields}} +{{ heading fields.headingLevel fields.heading }} +{{#each fields.value}} +{{ heading headingLevel heading }} + +{{#if description}} +{{{renderContent description}}} +{{/if}} + +**API Name** + +\`{{{apiName}}}\` + +**Type** + +*{{fieldType}}* + +{{#unless @last}}---{{/unless}} +{{/each}} +{{/if}} + +`.trim(); diff --git a/src/core/markdown/utils.ts b/src/core/markdown/utils.ts index 06f54064..39df2217 100644 --- a/src/core/markdown/utils.ts +++ b/src/core/markdown/utils.ts @@ -1,3 +1,5 @@ import { ParsedFile } from '../shared/types'; +import { Type } from '@cparra/apex-reflection'; -export const parsedFilesToTypes = (parsedFiles: ParsedFile[]) => parsedFiles.map((parsedFile) => parsedFile.type); +export const parsedFilesToTypes = (parsedFiles: ParsedFile[]) => + parsedFiles.map((parsedFile) => parsedFile.type); diff --git a/src/core/openapi/__tests__/open-api-docs-processor.spec.ts b/src/core/openapi/__tests__/open-api-docs-processor.spec.ts index 45817899..3951b896 100644 --- a/src/core/openapi/__tests__/open-api-docs-processor.spec.ts +++ b/src/core/openapi/__tests__/open-api-docs-processor.spec.ts @@ -1,5 +1,5 @@ import { OpenApiDocsProcessor } from '../open-api-docs-processor'; -import { OpenApiSettings } from '../../openApiSettings'; +import { OpenApiSettings } from '../openApiSettings'; import { SettingsBuilder } from '../../../test-helpers/SettingsBuilder'; import { DocCommentBuilder } from '../../../test-helpers/DocCommentBuilder'; import { AnnotationBuilder } from '../../../test-helpers/AnnotationBuilder'; diff --git a/src/core/openapi/manifest-factory.ts b/src/core/openapi/manifest-factory.ts index 9ba277a1..19124275 100644 --- a/src/core/openapi/manifest-factory.ts +++ b/src/core/openapi/manifest-factory.ts @@ -1,7 +1,7 @@ import Manifest from '../manifest'; import { TypeParser } from './parser'; import { ReflectionResult } from '@cparra/apex-reflection'; -import { UnparsedSourceFile } from '../shared/types'; +import { UnparsedApexBundle } from '../shared/types'; /** * Builds a new Manifest object, sourcing its types from the received TypeParser. @@ -10,7 +10,7 @@ import { UnparsedSourceFile } from '../shared/types'; */ export function createManifest( typeParser: TypeParser, - reflect: (apexBundle: UnparsedSourceFile) => ReflectionResult, + reflect: (apexBundle: UnparsedApexBundle) => ReflectionResult, ): Manifest { return new Manifest(typeParser.parse(reflect)); } diff --git a/src/core/openapi/open-api-docs-processor.ts b/src/core/openapi/open-api-docs-processor.ts index 5f52ae21..5f076348 100644 --- a/src/core/openapi/open-api-docs-processor.ts +++ b/src/core/openapi/open-api-docs-processor.ts @@ -2,7 +2,7 @@ import { FileContainer } from './file-container'; import { ClassMirror, Type } from '@cparra/apex-reflection'; import { Logger } from '#utils/logger'; import { OpenApi } from './open-api'; -import { OpenApiSettings } from '../openApiSettings'; +import { OpenApiSettings } from './openApiSettings'; import { MethodParser } from './parsers/MethodParser'; import { camel2title } from '#utils/string-utils'; import { createOpenApiFile } from './openapi-type-file'; diff --git a/src/core/openApiSettings.ts b/src/core/openapi/openApiSettings.ts similarity index 100% rename from src/core/openApiSettings.ts rename to src/core/openapi/openApiSettings.ts diff --git a/src/core/openapi/parser.ts b/src/core/openapi/parser.ts index 1ce9594b..ea34f47e 100644 --- a/src/core/openapi/parser.ts +++ b/src/core/openapi/parser.ts @@ -1,9 +1,9 @@ import { ClassMirror, InterfaceMirror, ReflectionResult, Type } from '@cparra/apex-reflection'; import { Logger } from '#utils/logger'; -import { UnparsedSourceFile } from '../shared/types'; +import { UnparsedApexBundle } from '../shared/types'; export interface TypeParser { - parse(reflect: (apexBundle: UnparsedSourceFile) => ReflectionResult): Type[]; + parse(reflect: (apexBundle: UnparsedApexBundle) => ReflectionResult): Type[]; } type NameAware = { name: string }; @@ -11,10 +11,10 @@ type NameAware = { name: string }; export class RawBodyParser implements TypeParser { constructor( private logger: Logger, - public typeBundles: UnparsedSourceFile[], + public typeBundles: UnparsedApexBundle[], ) {} - parse(reflect: (apexBundle: UnparsedSourceFile) => ReflectionResult): Type[] { + parse(reflect: (apexBundle: UnparsedApexBundle) => ReflectionResult): Type[] { const types = this.typeBundles .map((currentBundle) => { this.logger.log(`Parsing file: ${currentBundle.filePath}`); diff --git a/src/core/reflection/__test__/filter-scope.spec.ts b/src/core/reflection/apex/__test__/filter-scope.spec.ts similarity index 99% rename from src/core/reflection/__test__/filter-scope.spec.ts rename to src/core/reflection/apex/__test__/filter-scope.spec.ts index edf8f2b8..8eb5e782 100644 --- a/src/core/reflection/__test__/filter-scope.spec.ts +++ b/src/core/reflection/apex/__test__/filter-scope.spec.ts @@ -1,6 +1,7 @@ import { ClassMirror, EnumMirror, InterfaceMirror } from '@cparra/apex-reflection'; -import { filterScope } from '../filter-scope'; + import { parsedFileFromRawString } from './helpers'; +import { filterScope } from '../filter-scope'; describe('When filtering scope', () => { it('filters out files with the @ignore annotation', () => { diff --git a/src/core/reflection/__test__/helpers.ts b/src/core/reflection/apex/__test__/helpers.ts similarity index 73% rename from src/core/reflection/__test__/helpers.ts rename to src/core/reflection/apex/__test__/helpers.ts index 15ca7a53..f9d1a5a0 100644 --- a/src/core/reflection/__test__/helpers.ts +++ b/src/core/reflection/apex/__test__/helpers.ts @@ -1,7 +1,7 @@ -import { reflect } from '@cparra/apex-reflection'; -import { ParsedFile } from '../../shared/types'; +import { reflect, Type } from '@cparra/apex-reflection'; +import { ParsedFile } from '../../../shared/types'; -export function parsedFileFromRawString(raw: string): ParsedFile { +export function parsedFileFromRawString(raw: string): ParsedFile { const { error, typeMirror } = reflect(raw); if (error) { throw new Error(error.message); diff --git a/src/core/reflection/__test__/remove-excluded-tags.spec.ts b/src/core/reflection/apex/__test__/remove-excluded-tags.spec.ts similarity index 100% rename from src/core/reflection/__test__/remove-excluded-tags.spec.ts rename to src/core/reflection/apex/__test__/remove-excluded-tags.spec.ts index 108e5c14..83daa879 100644 --- a/src/core/reflection/__test__/remove-excluded-tags.spec.ts +++ b/src/core/reflection/apex/__test__/remove-excluded-tags.spec.ts @@ -1,6 +1,6 @@ import { parsedFileFromRawString } from './helpers'; -import { removeExcludedTags } from '../remove-excluded-tags'; import { ClassMirror, InterfaceMirror } from '@cparra/apex-reflection'; +import { removeExcludedTags } from '../remove-excluded-tags'; describe('when removing excluded tags', () => { describe('from any type', () => { diff --git a/src/core/reflection/filter-scope.ts b/src/core/reflection/apex/filter-scope.ts similarity index 64% rename from src/core/reflection/filter-scope.ts rename to src/core/reflection/apex/filter-scope.ts index bb9c153e..2f0fa9b3 100644 --- a/src/core/reflection/filter-scope.ts +++ b/src/core/reflection/apex/filter-scope.ts @@ -1,7 +1,8 @@ -import Manifest from '../manifest'; -import { ParsedFile } from '../shared/types'; +import Manifest from '../../manifest'; +import { ParsedFile } from '../../shared/types'; +import { Type } from '@cparra/apex-reflection'; -export function filterScope(scopes: string[], parsedFiles: ParsedFile[]): ParsedFile[] { +export function filterScope(scopes: string[], parsedFiles: ParsedFile[]): ParsedFile[] { return parsedFiles .filter(({ type }) => Manifest.shouldFilterType(type, scopes)) .map((parsedFile) => { diff --git a/src/core/reflection/inheritance-chain-expanion.ts b/src/core/reflection/apex/inheritance-chain-expanion.ts similarity index 82% rename from src/core/reflection/inheritance-chain-expanion.ts rename to src/core/reflection/apex/inheritance-chain-expanion.ts index 108b84a8..77da29b0 100644 --- a/src/core/reflection/inheritance-chain-expanion.ts +++ b/src/core/reflection/apex/inheritance-chain-expanion.ts @@ -1,9 +1,9 @@ import { ClassMirror, Type } from '@cparra/apex-reflection'; import { createInheritanceChain } from './inheritance-chain'; -import { parsedFilesToTypes } from '../markdown/utils'; -import { ParsedFile } from '../shared/types'; +import { parsedFilesToTypes } from '../../markdown/utils'; +import { ParsedFile } from '../../shared/types'; -export const addInheritanceChainToTypes = (parsedFiles: ParsedFile[]): ParsedFile[] => +export const addInheritanceChainToTypes = (parsedFiles: ParsedFile[]): ParsedFile[] => parsedFiles.map((parsedFile) => ({ ...parsedFile, type: addInheritanceChain(parsedFile.type, parsedFilesToTypes(parsedFiles)), diff --git a/src/core/reflection/inheritance-chain.ts b/src/core/reflection/apex/inheritance-chain.ts similarity index 100% rename from src/core/reflection/inheritance-chain.ts rename to src/core/reflection/apex/inheritance-chain.ts diff --git a/src/core/reflection/inherited-member-expansion.ts b/src/core/reflection/apex/inherited-member-expansion.ts similarity index 95% rename from src/core/reflection/inherited-member-expansion.ts rename to src/core/reflection/apex/inherited-member-expansion.ts index 0cfba92a..0df9db3d 100644 --- a/src/core/reflection/inherited-member-expansion.ts +++ b/src/core/reflection/apex/inherited-member-expansion.ts @@ -1,12 +1,12 @@ import { ClassMirror, InterfaceMirror, Type } from '@cparra/apex-reflection'; import { pipe } from 'fp-ts/function'; -import { ParsedFile } from '../shared/types'; -import { parsedFilesToTypes } from '../markdown/utils'; +import { ParsedFile } from '../../shared/types'; +import { parsedFilesToTypes } from '../../markdown/utils'; -export const addInheritedMembersToTypes = (parsedFiles: ParsedFile[]) => +export const addInheritedMembersToTypes = (parsedFiles: ParsedFile[]) => parsedFiles.map((parsedFile) => addInheritedMembers(parsedFilesToTypes(parsedFiles), parsedFile)); -export function addInheritedMembers(repository: Type[], parsedFile: ParsedFile): ParsedFile { +export function addInheritedMembers(repository: Type[], parsedFile: ParsedFile): ParsedFile { function addInheritedMembersToType(repository: Type[], current: T): T { if (current.type_name === 'enum') { return current; diff --git a/src/core/parse-apex-metadata.ts b/src/core/reflection/apex/parse-apex-metadata.ts similarity index 100% rename from src/core/parse-apex-metadata.ts rename to src/core/reflection/apex/parse-apex-metadata.ts diff --git a/src/core/reflection/reflect-source.ts b/src/core/reflection/apex/reflect-apex-source.ts similarity index 80% rename from src/core/reflection/reflect-source.ts rename to src/core/reflection/apex/reflect-apex-source.ts index f83199ee..67206fcb 100644 --- a/src/core/reflection/reflect-source.ts +++ b/src/core/reflection/apex/reflect-apex-source.ts @@ -8,9 +8,9 @@ import * as O from 'fp-ts/Option'; import { ParsingError } from '@cparra/apex-reflection'; import { apply } from '#utils/fp'; import { Semigroup } from 'fp-ts/Semigroup'; -import { ParsedFile, UnparsedSourceFile } from '../shared/types'; -import { ReflectionError, ReflectionErrors } from '../errors/errors'; -import { parseApexMetadata } from '../parse-apex-metadata'; +import { ParsedFile, UnparsedApexBundle } from '../../shared/types'; +import { ReflectionError, ReflectionErrors } from '../../errors/errors'; +import { parseApexMetadata } from './parse-apex-metadata'; async function reflectAsync(rawSource: string): Promise { return new Promise((resolve, reject) => { @@ -25,7 +25,7 @@ async function reflectAsync(rawSource: string): Promise { }); } -export function reflectBundles(apexBundles: UnparsedSourceFile[]) { +export function reflectApexSource(apexBundles: UnparsedApexBundle[]) { const semiGroupReflectionError: Semigroup = { concat: (x, y) => new ReflectionErrors([...x.errors, ...y.errors]), }; @@ -34,14 +34,14 @@ export function reflectBundles(apexBundles: UnparsedSourceFile[]) { return pipe(apexBundles, A.traverse(Ap)(reflectBundle)); } -function reflectBundle(apexBundle: UnparsedSourceFile): TE.TaskEither { - const convertToParsedFile: (typeMirror: Type) => ParsedFile = apply(toParsedFile, apexBundle.filePath); +function reflectBundle(apexBundle: UnparsedApexBundle): TE.TaskEither> { + const convertToParsedFile: (typeMirror: Type) => ParsedFile = apply(toParsedFile, apexBundle.filePath); const withMetadata = apply(addMetadata, apexBundle.metadataContent); return pipe(apexBundle, reflectAsTask, TE.map(convertToParsedFile), TE.flatMap(withMetadata)); } -function reflectAsTask(apexBundle: UnparsedSourceFile): TE.TaskEither { +function reflectAsTask(apexBundle: UnparsedApexBundle): TE.TaskEither { return TE.tryCatch( () => reflectAsync(apexBundle.content), (error) => @@ -49,7 +49,7 @@ function reflectAsTask(apexBundle: UnparsedSourceFile): TE.TaskEither { return { source: { filePath: filePath, @@ -62,12 +62,12 @@ function toParsedFile(filePath: string, typeMirror: Type): ParsedFile { function addMetadata( rawMetadataContent: string | null, - parsedFile: ParsedFile, -): TE.TaskEither { + parsedFile: ParsedFile, +): TE.TaskEither> { return TE.fromEither( pipe( parsedFile.type, - (type) => addFileMetadataToTypeAnnotation(type, rawMetadataContent), + (type) => addFileMetadataToTypeAnnotation(type as Type, rawMetadataContent), E.map((type) => ({ ...parsedFile, type })), E.mapLeft((error) => errorToReflectionErrors(error, parsedFile.source.filePath)), ), diff --git a/src/core/reflection/remove-excluded-tags.ts b/src/core/reflection/apex/remove-excluded-tags.ts similarity index 98% rename from src/core/reflection/remove-excluded-tags.ts rename to src/core/reflection/apex/remove-excluded-tags.ts index 9c6a4b1d..fa88c66e 100644 --- a/src/core/reflection/remove-excluded-tags.ts +++ b/src/core/reflection/apex/remove-excluded-tags.ts @@ -3,13 +3,13 @@ import { match } from 'fp-ts/boolean'; import { ClassMirror, DocComment, InterfaceMirror, Type } from '@cparra/apex-reflection'; import { pipe } from 'fp-ts/function'; import { apply } from '#utils/fp'; -import { ParsedFile } from '../shared/types'; +import { ParsedFile } from '../../shared/types'; type AppliedRemoveTagFn = (tagName: string, removeFn: RemoveTagFn) => DocComment; type RemoveTagFn = (docComment: DocComment) => DocComment; type Documentable = { docComment?: DocComment }; -export const removeExcludedTags = (excludedTags: string[], parsedFiles: ParsedFile[]): ParsedFile[] => { +export const removeExcludedTags = (excludedTags: string[], parsedFiles: ParsedFile[]): ParsedFile[] => { return parsedFiles.map((parsedFile) => { return { ...parsedFile, diff --git a/src/core/reflection/sobject/__test__/reflect-custom-field-sources.spec.ts b/src/core/reflection/sobject/__test__/reflect-custom-field-sources.spec.ts new file mode 100644 index 00000000..0eba7e3c --- /dev/null +++ b/src/core/reflection/sobject/__test__/reflect-custom-field-sources.spec.ts @@ -0,0 +1,202 @@ +import { UnparsedCustomFieldBundle } from '../../../shared/types'; +import { reflectCustomFieldSources } from '../reflect-custom-field-source'; +import { assertEither } from '../../../test-helpers/assert-either'; +import * as E from 'fp-ts/Either'; + +const customFieldContent = ` + + + PhotoUrl__c + false + + false + false + Url + A Photo URL field +`; + +describe('when parsing custom field metadata', () => { + test('the resulting type contains the file path', async () => { + const unparsed: UnparsedCustomFieldBundle = { + type: 'customfield', + name: 'PhotoUrl__c', + parentName: 'MyFirstObject__c', + filePath: 'src/field/PhotoUrl__c.field-meta.xml', + content: customFieldContent, + }; + + const result = await reflectCustomFieldSources([unparsed])(); + + assertEither(result, (data) => expect(data[0].source.filePath).toBe('src/field/PhotoUrl__c.field-meta.xml')); + }); + + test('the resulting type contains the correct name', async () => { + const unparsed: UnparsedCustomFieldBundle = { + type: 'customfield', + name: 'PhotoUrl__c', + parentName: 'MyFirstObject__c', + filePath: 'src/field/PhotoUrl__c.field-meta.xml', + content: customFieldContent, + }; + + const result = await reflectCustomFieldSources([unparsed])(); + + assertEither(result, (data) => expect(data[0].type.name).toBe('PhotoUrl__c')); + }); + + test('the resulting type contains the correct parent name', async () => { + const unparsed: UnparsedCustomFieldBundle = { + type: 'customfield', + name: 'PhotoUrl__c', + parentName: 'MyFirstObject__c', + filePath: 'src/field/PhotoUrl__c.field-meta.xml', + content: customFieldContent, + }; + + const result = await reflectCustomFieldSources([unparsed])(); + + assertEither(result, (data) => expect(data[0].type.parentName).toBe('MyFirstObject__c')); + }); + + test('the resulting type contains the correct label', async () => { + const unparsed: UnparsedCustomFieldBundle = { + type: 'customfield', + name: 'PhotoUrl__c', + parentName: 'MyFirstObject__c', + filePath: 'src/field/PhotoUrl__c.field-meta.xml', + content: customFieldContent, + }; + + const result = await reflectCustomFieldSources([unparsed])(); + + assertEither(result, (data) => expect(data[0].type.label).toBe('PhotoUrl')); + }); + + test('the resulting type contains the correct type', async () => { + const unparsed: UnparsedCustomFieldBundle = { + type: 'customfield', + name: 'PhotoUrl__c', + parentName: 'MyFirstObject__c', + filePath: 'src/field/PhotoUrl__c.field-meta.xml', + content: customFieldContent, + }; + + const result = await reflectCustomFieldSources([unparsed])(); + + assertEither(result, (data) => expect(data[0].type.type).toBe('Url')); + }); + + test('the resulting type contains the correct description', async () => { + const unparsed: UnparsedCustomFieldBundle = { + type: 'customfield', + name: 'PhotoUrl__c', + parentName: 'MyFirstObject__c', + filePath: 'src/field/PhotoUrl__c.field-meta.xml', + content: customFieldContent, + }; + + const result = await reflectCustomFieldSources([unparsed])(); + + assertEither(result, (data) => expect(data[0].type.description).toBe('A Photo URL field')); + }); + + test('An error is returned when the XML is in an invalid format', async () => { + const unparsed: UnparsedCustomFieldBundle = { + type: 'customfield', + name: 'PhotoUrl__c', + parentName: 'MyFirstObject__c', + filePath: 'src/field/PhotoUrl__c.field-meta.xml', + content: 'invalid-xml', + }; + + const result = await reflectCustomFieldSources([unparsed])(); + + expect(E.isLeft(result)).toBe(true); + }); + + test('An error is returned when the XML is missing the CustomField key', async () => { + const unparsed: UnparsedCustomFieldBundle = { + type: 'customfield', + name: 'PhotoUrl__c', + parentName: 'MyFirstObject__c', + filePath: 'src/field/PhotoUrl__c.field-meta.xml', + content: ` + + + PhotoUrl__c + false + + false + false + Url + A Photo URL field + `, + }; + + const result = await reflectCustomFieldSources([unparsed])(); + + expect(E.isLeft(result)).toBe(true); + }); + + test('An error is returned when the CustomField key is not an object', async () => { + const unparsed: UnparsedCustomFieldBundle = { + type: 'customfield', + name: 'PhotoUrl__c', + parentName: 'MyFirstObject__c', + filePath: 'src/field/PhotoUrl__c.field-meta.xml', + content: ` + + invalid`, + }; + + const result = await reflectCustomFieldSources([unparsed])(); + + expect(E.isLeft(result)).toBe(true); + }); + + test('An error is returned when the CustomKey object does not contain the label key', async () => { + const unparsed: UnparsedCustomFieldBundle = { + type: 'customfield', + name: 'PhotoUrl__c', + parentName: 'MyFirstObject__c', + filePath: 'src/field/PhotoUrl__c.field-meta.xml', + content: ` + + + PhotoUrl__c + false + false + false + Url + A Photo URL field + `, + }; + + const result = await reflectCustomFieldSources([unparsed])(); + + expect(E.isLeft(result)).toBe(true); + }); + + test('An error is returned when the CustomKey object does not contain the type key', async () => { + const unparsed: UnparsedCustomFieldBundle = { + type: 'customfield', + name: 'PhotoUrl__c', + parentName: 'MyFirstObject__c', + filePath: 'src/field/PhotoUrl__c.field-meta.xml', + content: ` + + + PhotoUrl__c + false + + false + false + A Photo URL field + `, + }; + + const result = await reflectCustomFieldSources([unparsed])(); + + expect(E.isLeft(result)).toBe(true); + }); +}); diff --git a/src/core/reflection/sobject/__test__/reflect-custom-object-sources.spec.ts b/src/core/reflection/sobject/__test__/reflect-custom-object-sources.spec.ts new file mode 100644 index 00000000..f923feee --- /dev/null +++ b/src/core/reflection/sobject/__test__/reflect-custom-object-sources.spec.ts @@ -0,0 +1,181 @@ +import { reflectCustomObjectSources } from '../reflect-custom-object-sources'; +import { UnparsedCustomObjectBundle } from '../../../shared/types'; +import { assertEither } from '../../../test-helpers/assert-either'; +import * as E from 'fp-ts/Either'; + +const sObjectContent = ` + + + Deployed + test object for testing + + MyFirstObjects + Public + `; + +describe('when parsing SObject metadata', () => { + test('the resulting type contains the file path', async () => { + const unparsed: UnparsedCustomObjectBundle = { + type: 'customobject', + name: 'MyFirstObject__c', + filePath: 'src/object/MyFirstObject__c.object-meta.xml', + content: sObjectContent, + }; + + const result = await reflectCustomObjectSources([unparsed])(); + + assertEither(result, (data) => expect(data[0].source.filePath).toBe('src/object/MyFirstObject__c.object-meta.xml')); + }); + + test('the resulting type contains the correct label', async () => { + const unparsed: UnparsedCustomObjectBundle = { + type: 'customobject', + name: 'MyFirstObject__c', + filePath: 'src/object/MyFirstObject__c.object-meta.xml', + content: sObjectContent, + }; + + const result = await reflectCustomObjectSources([unparsed])(); + + assertEither(result, (data) => { + expect(data[0].type.label).toBe('MyFirstObject'); + }); + }); + + test('the resulting type contains the correct name', async () => { + const unparsed: UnparsedCustomObjectBundle = { + type: 'customobject', + name: 'MyFirstObject__c', + filePath: 'src/object/MyFirstObject__c.object-meta.xml', + content: sObjectContent, + }; + + const result = await reflectCustomObjectSources([unparsed])(); + + assertEither(result, (data) => { + expect(data[0].type.name).toBe('MyFirstObject__c'); + }); + }); + + test('the resulting type contains the deployment status', async () => { + const unparsed: UnparsedCustomObjectBundle = { + type: 'customobject', + name: 'MyFirstObject__c', + filePath: 'src/object/MyFirstObject__c.object-meta.xml', + content: sObjectContent, + }; + + const result = await reflectCustomObjectSources([unparsed])(); + + assertEither(result, (data) => { + expect(data[0].type.deploymentStatus).toBe('Deployed'); + }); + }); + + test('the deployment status is "Deployed" by default', async () => { + const sObjectContent = ` + + + test object for testing + + MyFirstObjects + `; + + const unparsed: UnparsedCustomObjectBundle = { + type: 'customobject', + name: 'MyFirstObject__c', + filePath: 'src/object/MyFirstObject__c.object-meta.xml', + content: sObjectContent, + }; + + const result = await reflectCustomObjectSources([unparsed])(); + + assertEither(result, (data) => { + expect(data[0].type.deploymentStatus).toBe('Deployed'); + }); + }); + + test('the resulting type contains the visibility', async () => { + const unparsed: UnparsedCustomObjectBundle = { + type: 'customobject', + name: 'MyFirstObject__c', + filePath: 'src/object/MyFirstObject__c.object-meta.xml', + content: sObjectContent, + }; + + const result = await reflectCustomObjectSources([unparsed])(); + + assertEither(result, (data) => { + expect(data[0].type.visibility).toBe('Public'); + }); + }); + + test('the visibility is "Public" by default', async () => { + const sObjectContent = ` + + + Deployed + test object for testing + + MyFirstObjects + `; + + const unparsed: UnparsedCustomObjectBundle = { + type: 'customobject', + name: 'MyFirstObject__c', + filePath: 'src/object/MyFirstObject__c.object-meta.xml', + content: sObjectContent, + }; + + const result = await reflectCustomObjectSources([unparsed])(); + + assertEither(result, (data) => { + expect(data[0].type.visibility).toBe('Public'); + }); + }); + + test('an error is thrown when the XML is in an invalid format', async () => { + const sObjectContent = ` + + + Deployed + test object for testing + + MyFirstObjects + Public + `; + + const unparsed: UnparsedCustomObjectBundle = { + type: 'customobject', + name: 'MyFirstObject__c', + filePath: 'src/object/MyFirstObject__c.object-meta.xml', + content: sObjectContent, + }; + + const result = await reflectCustomObjectSources([unparsed])(); + + expect(E.isLeft(result)).toBe(true); + }); + + test('an error is thrown when the label is missing', async () => { + const sObjectContent = ` + + + Deployed + test object for testing + MyFirstObjects + Public + `; + + const unparsed: UnparsedCustomObjectBundle = { + type: 'customobject', + name: 'MyFirstObject__c', + filePath: 'src/object/MyFirstObject__c.object-meta.xml', + content: sObjectContent, + }; + + const result = await reflectCustomObjectSources([unparsed])(); + + expect(E.isLeft(result)).toBe(true); + }); +}); diff --git a/src/core/reflection/sobject/reflect-custom-field-source.ts b/src/core/reflection/sobject/reflect-custom-field-source.ts new file mode 100644 index 00000000..a0affa80 --- /dev/null +++ b/src/core/reflection/sobject/reflect-custom-field-source.ts @@ -0,0 +1,104 @@ +import { ParsedFile, UnparsedCustomFieldBundle } from '../../shared/types'; +import { ReflectionError, ReflectionErrors } from '../../errors/errors'; +import { Semigroup } from 'fp-ts/Semigroup'; +import * as TE from 'fp-ts/TaskEither'; +import * as T from 'fp-ts/Task'; +import { pipe } from 'fp-ts/function'; +import * as A from 'fp-ts/Array'; +import { XMLParser } from 'fast-xml-parser'; +import * as E from 'fp-ts/Either'; + +export type CustomFieldMetadata = { + type_name: 'customfield'; + description: string | null; + name: string; + label: string; + type: string; + parentName: string; +}; + +export function reflectCustomFieldSources( + customFieldSources: UnparsedCustomFieldBundle[], +): TE.TaskEither[]> { + const semiGroupReflectionError: Semigroup = { + concat: (x, y) => new ReflectionErrors([...x.errors, ...y.errors]), + }; + const Ap = TE.getApplicativeTaskValidation(T.ApplyPar, semiGroupReflectionError); + + return pipe(customFieldSources, A.traverse(Ap)(reflectCustomFieldSource)); +} + +function reflectCustomFieldSource( + customFieldSource: UnparsedCustomFieldBundle, +): TE.TaskEither> { + return pipe( + E.tryCatch(() => new XMLParser().parse(customFieldSource.content), E.toError), + E.flatMap(validate), + E.map(toCustomFieldMetadata), + E.map((metadata) => addName(metadata, customFieldSource.name)), + E.map((metadata) => addParentName(metadata, customFieldSource.parentName)), + E.map((metadata) => toParsedFile(customFieldSource.filePath, metadata)), + E.mapLeft((error) => new ReflectionErrors([new ReflectionError(customFieldSource.filePath, error.message)])), + TE.fromEither, + ); +} + +function validate(parsedResult: unknown): E.Either { + const err = E.left(new Error('Invalid custom field metadata')); + + function isObject(value: unknown) { + return typeof value === 'object' && value !== null ? E.right(value) : err; + } + + function hasTheCustomFieldKey(value: object) { + return 'CustomField' in value ? E.right(value) : err; + } + + function theCustomFieldKeyIsAnObject(value: Record<'CustomField', unknown>) { + return typeof value.CustomField === 'object' ? E.right(value as Record<'CustomField', object>) : err; + } + + function theCustomFieldObjectContainsTheLabelKey(value: Record<'CustomField', object>) { + return 'label' in value.CustomField ? E.right(value) : err; + } + + function theCustomFieldObjectContainsTheTypeKey(value: Record<'CustomField', object>) { + return 'type' in value.CustomField ? E.right(value) : err; + } + + return pipe( + parsedResult, + isObject, + E.chain(hasTheCustomFieldKey), + E.chain(theCustomFieldKeyIsAnObject), + E.chain(theCustomFieldObjectContainsTheLabelKey), + E.chain(theCustomFieldObjectContainsTheTypeKey), + ); +} + +function toCustomFieldMetadata(parserResult: { CustomField: object }): CustomFieldMetadata { + const defaultValues = { + description: null, + }; + + return { ...defaultValues, ...parserResult.CustomField, type_name: 'customfield' } as CustomFieldMetadata; +} + +function addName(metadata: CustomFieldMetadata, name: string): CustomFieldMetadata { + return { ...metadata, name }; +} + +function addParentName(metadata: CustomFieldMetadata, parentName: string): CustomFieldMetadata { + return { ...metadata, parentName }; +} + +function toParsedFile(filePath: string, typeMirror: CustomFieldMetadata): ParsedFile { + return { + source: { + filePath, + name: typeMirror.name, + type: typeMirror.type_name, + }, + type: typeMirror, + }; +} diff --git a/src/core/reflection/sobject/reflect-custom-object-sources.ts b/src/core/reflection/sobject/reflect-custom-object-sources.ts new file mode 100644 index 00000000..e301c4c1 --- /dev/null +++ b/src/core/reflection/sobject/reflect-custom-object-sources.ts @@ -0,0 +1,109 @@ +import { ParsedFile, UnparsedCustomObjectBundle } from '../../shared/types'; +import { XMLParser } from 'fast-xml-parser'; +import * as TE from 'fp-ts/TaskEither'; +import { ReflectionError, ReflectionErrors } from '../../errors/errors'; +import { Semigroup } from 'fp-ts/Semigroup'; +import * as T from 'fp-ts/Task'; +import { pipe } from 'fp-ts/function'; +import * as A from 'fp-ts/Array'; +import * as E from 'fp-ts/Either'; +import { CustomFieldMetadata } from './reflect-custom-field-source'; + +export type ObjectMetadata = { + type_name: 'customobject'; + deploymentStatus: string; + visibility: string; + label: string; + name: string; + description: string | null; + fields: ParsedFile[]; +}; + +export function reflectCustomObjectSources( + objectSources: UnparsedCustomObjectBundle[], +): TE.TaskEither[]> { + const semiGroupReflectionError: Semigroup = { + concat: (x, y) => new ReflectionErrors([...x.errors, ...y.errors]), + }; + const Ap = TE.getApplicativeTaskValidation(T.ApplyPar, semiGroupReflectionError); + + return pipe(objectSources, A.traverse(Ap)(reflectCustomObjectSource)); +} + +function reflectCustomObjectSource( + objectSource: UnparsedCustomObjectBundle, +): TE.TaskEither> { + return pipe( + E.tryCatch(() => new XMLParser().parse(objectSource.content), E.toError), + E.flatMap(validate), + E.map(toObjectMetadata), + E.map((metadata) => addName(metadata, objectSource.name)), + E.map(addTypeName), + E.map((metadata) => toParsedFile(objectSource.filePath, metadata)), + E.mapLeft((error) => new ReflectionErrors([new ReflectionError(objectSource.filePath, error.message)])), + TE.fromEither, + ); +} + +function validate(parseResult: unknown): E.Either { + const err = E.left(new Error('Invalid SObject metadata')); + + function isObject(value: unknown) { + return typeof value === 'object' && value !== null ? E.right(value) : err; + } + + function hasTheCustomObjectKey(value: object) { + return 'CustomObject' in value ? E.right(value) : err; + } + + function theCustomObjectKeyIsAnObject(value: Record<'CustomObject', unknown>) { + return typeof value.CustomObject === 'object' ? E.right(value as Record<'CustomObject', object>) : err; + } + + function theCustomObjectContainsTheLabelKey(value: Record<'CustomObject', object>) { + return 'label' in value.CustomObject ? E.right(value) : err; + } + + return pipe( + parseResult, + isObject, + E.chain(hasTheCustomObjectKey), + E.chain(theCustomObjectKeyIsAnObject), + E.chain(theCustomObjectContainsTheLabelKey), + ); +} + +function toObjectMetadata(parserResult: { CustomObject: object }): ObjectMetadata { + const defaultValues = { + deploymentStatus: 'Deployed', + visibility: 'Public', + description: null, + fields: [] as ParsedFile[], + }; + return { ...defaultValues, ...parserResult.CustomObject } as ObjectMetadata; +} + +function addName(objectMetadata: ObjectMetadata, name: string): ObjectMetadata { + return { + ...objectMetadata, + name, + }; +} + +function addTypeName(objectMetadata: ObjectMetadata): ObjectMetadata { + return { + ...objectMetadata, + type_name: 'customobject', + }; +} + +function toParsedFile(filePath: string, typeMirror: ObjectMetadata): ParsedFile { + return { + source: { + filePath: filePath, + name: typeMirror.name, + type: typeMirror.type_name, + }, + type: typeMirror, + }; +} diff --git a/src/core/reflection/sort-types-and-members.ts b/src/core/reflection/sort-types-and-members.ts index 9c2e3e8e..fde5f41e 100644 --- a/src/core/reflection/sort-types-and-members.ts +++ b/src/core/reflection/sort-types-and-members.ts @@ -1,13 +1,21 @@ import { ClassMirror, EnumMirror, InterfaceMirror, Type } from '@cparra/apex-reflection'; import { ParsedFile } from '../shared/types'; +import { isApexType } from '../shared/utils'; +import { ObjectMetadata } from './sobject/reflect-custom-object-sources'; +import { CustomFieldMetadata } from './sobject/reflect-custom-field-source'; type Named = { name: string }; -export function sortTypesAndMembers(shouldSort: boolean, parsedFiles: ParsedFile[]): ParsedFile[] { +export function sortTypesAndMembers( + shouldSort: boolean, + parsedFiles: ParsedFile[], +): ParsedFile[] { return parsedFiles .map((parsedFile) => ({ ...parsedFile, - type: sortTypeMember(parsedFile.type, shouldSort), + type: isApexType(parsedFile.type) + ? sortTypeMember(parsedFile.type, shouldSort) + : sortCustomObjectFields(parsedFile.type, shouldSort), })) .sort((a, b) => sortByNames(shouldSort, a.type, b.type)); } @@ -34,6 +42,17 @@ function sortTypeMember(type: Type, shouldSort: boolean): Type { } } +function sortCustomObjectFields(type: ObjectMetadata, shouldSort: boolean): ObjectMetadata { + return { + ...type, + fields: sortFields(type.fields, shouldSort), + }; +} + +function sortFields(fields: ParsedFile[], shouldSort: boolean): ParsedFile[] { + return fields.sort((a, b) => sortByNames(shouldSort, a.type, b.type)); +} + function sortEnumValues(shouldSort: boolean, enumType: EnumMirror): EnumMirror { return { ...enumType, diff --git a/src/core/renderables/types.d.ts b/src/core/renderables/types.d.ts index 878bc918..ddf50728 100644 --- a/src/core/renderables/types.d.ts +++ b/src/core/renderables/types.d.ts @@ -82,7 +82,7 @@ type RenderableDocumentation = { annotations?: Annotation[]; description?: RenderableContent[]; customTags?: CustomTag[]; - example: RenderableSection; + example?: RenderableSection; group?: string; author?: string; date?: string; @@ -131,7 +131,7 @@ type RenderableMethod = { inherited?: boolean; }; -type RenderableField = { +type RenderableApexField = { headingLevel: number; heading: string; type: RenderableSection; @@ -159,8 +159,8 @@ export type RenderableClass = RenderableType & { isGrouped: boolean; }; methods: RenderableSection[]> & { isGrouped: boolean }; - fields: RenderableSection[]> & { isGrouped: boolean }; - properties: RenderableSection[]> & { isGrouped: boolean }; + fields: RenderableSection[]> & { isGrouped: boolean }; + properties: RenderableSection[]> & { isGrouped: boolean }; innerClasses: RenderableSection; innerEnums: RenderableSection; innerInterfaces: RenderableSection; @@ -177,4 +177,22 @@ export type RenderableEnum = RenderableType & { values: RenderableSection; }; -export type Renderable = (RenderableClass | RenderableInterface | RenderableEnum) & { filePath: string }; +export type RenderableCustomObject = Omit & { + apiName: string; + type: 'customobject'; + hasFields: boolean; + fields: RenderableSection; +}; + +export type RenderableCustomField = { + headingLevel: number; + heading: string; + apiName: string; + description: RenderableContent[]; + type: 'field'; + fieldType: string; +}; + +export type Renderable = (RenderableClass | RenderableInterface | RenderableEnum | RenderableCustomObject) & { + filePath: string; +}; diff --git a/src/core/shared/types.d.ts b/src/core/shared/types.d.ts index 74d31854..a3c5cac1 100644 --- a/src/core/shared/types.d.ts +++ b/src/core/shared/types.d.ts @@ -1,5 +1,7 @@ import { Type } from '@cparra/apex-reflection'; import { ChangeLogPageData } from '../changelog/generate-change-log'; +import { ObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; +import { CustomFieldMetadata } from '../reflection/sobject/reflect-custom-field-source'; export type Generators = 'markdown' | 'openapi' | 'changelog'; @@ -21,11 +23,13 @@ export type UserDefinedMarkdownConfig = { scope: string[]; namespace?: string; defaultGroupName: string; + customObjectsGroupName: string; sortAlphabetically: boolean; includeMetadata: boolean; linkingStrategy: LinkingStrategy; excludeTags: string[]; referenceGuideTitle: string; + /** Glob patterns to exclude files from the documentation. */ exclude: string[]; } & Partial; @@ -53,7 +57,26 @@ export type UserDefinedChangelogConfig = { export type UserDefinedConfig = UserDefinedMarkdownConfig | UserDefinedOpenApiConfig | UserDefinedChangelogConfig; -export type UnparsedSourceFile = { +export type UnparsedSourceBundle = UnparsedApexBundle | UnparsedCustomObjectBundle | UnparsedCustomFieldBundle; + +export type UnparsedCustomObjectBundle = { + type: 'customobject'; + name: string; + filePath: string; + content: string; +}; + +export type UnparsedCustomFieldBundle = { + type: 'customfield'; + name: string; + filePath: string; + content: string; + parentName: string; +}; + +export type UnparsedApexBundle = { + type: 'apex'; + name: string; filePath: string; content: string; metadataContent: string | null; @@ -62,12 +85,14 @@ export type UnparsedSourceFile = { export type SourceFileMetadata = { filePath: string; name: string; - type: 'interface' | 'class' | 'enum'; + type: 'interface' | 'class' | 'enum' | 'customobject' | 'customfield'; }; -export type ParsedFile = { +export type ParsedFile< + T extends Type | ObjectMetadata | CustomFieldMetadata = Type | ObjectMetadata | CustomFieldMetadata, +> = { source: SourceFileMetadata; - type: Type; + type: T; }; export type DocPageReference = { @@ -84,7 +109,7 @@ export type DocPageReference = { referencePath: string; }; -type Frontmatter = string | Record | null; +export type Frontmatter = string | Record | null; export type ReferenceGuidePageData = { frontmatter: Frontmatter; @@ -98,9 +123,10 @@ export type DocPageData = { outputDocPath: string; frontmatter: Frontmatter; content: string; + type: 'class' | 'interface' | 'enum' | 'customobject'; }; -export type OpenApiPageData = Omit; +export type OpenApiPageData = Omit; export type PageData = DocPageData | OpenApiPageData | ReferenceGuidePageData | ChangeLogPageData; diff --git a/src/core/shared/utils.ts b/src/core/shared/utils.ts index 233b0899..12982c74 100644 --- a/src/core/shared/utils.ts +++ b/src/core/shared/utils.ts @@ -1,4 +1,8 @@ import { Skip } from './types'; +import { Type } from '@cparra/apex-reflection'; +import { ObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; +import { MarkdownGeneratorConfig } from '../markdown/generate-docs'; +import { CustomFieldMetadata } from '../reflection/sobject/reflect-custom-field-source'; /** * Represents a file to be skipped. @@ -12,3 +16,27 @@ export function skip(): Skip { export function isSkip(value: unknown): value is Skip { return Object.prototype.hasOwnProperty.call(value, '_tag') && (value as Skip)._tag === 'Skip'; } + +export function isObjectType(type: Type | ObjectMetadata | CustomFieldMetadata): type is ObjectMetadata { + return (type as ObjectMetadata).type_name === 'customobject'; +} + +export function isApexType(type: Type | ObjectMetadata | CustomFieldMetadata): type is Type { + return !isObjectType(type); +} + +export function getTypeGroup(type: Type | ObjectMetadata, config: MarkdownGeneratorConfig): string { + function getGroup(type: Type, config: MarkdownGeneratorConfig): string { + const groupAnnotation = type.docComment?.annotations.find( + (annotation) => annotation.name.toLowerCase() === 'group', + ); + return groupAnnotation?.body ?? config.defaultGroupName; + } + + switch (type.type_name) { + case 'customobject': + return config.customObjectsGroupName; + default: + return getGroup(type, config); + } +} diff --git a/src/defaults.ts b/src/defaults.ts index 0182cb1f..8510cf7a 100644 --- a/src/defaults.ts +++ b/src/defaults.ts @@ -6,10 +6,11 @@ export const markdownDefaults = { ...commonDefaults, scope: ['global'], defaultGroupName: 'Miscellaneous', + customObjectsGroupName: 'Custom Objects', includeMetadata: false, sortAlphabetically: false, linkingStrategy: 'relative' as const, - referenceGuideTitle: 'Apex Reference Guide', + referenceGuideTitle: 'Reference Guide', excludeTags: [], exclude: [], }; diff --git a/src/test-helpers/SettingsBuilder.ts b/src/test-helpers/SettingsBuilder.ts index 72738598..64e992aa 100644 --- a/src/test-helpers/SettingsBuilder.ts +++ b/src/test-helpers/SettingsBuilder.ts @@ -1,4 +1,4 @@ -import { SettingsConfig } from '../core/openApiSettings'; +import { SettingsConfig } from '../core/openapi/openApiSettings'; /** * Builder class to create SettingsConfig objects.