@pega/custom-dx-components 26.1.11 → 26.1.12-alpha.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/package.json +48 -64
- package/src/ai/README.md +44 -0
- package/src/ai/catalog/README.md +133 -0
- package/src/ai/catalog/best-practices/README.md +24 -0
- package/src/ai/catalog/best-practices/accessibility-for-async-components.md +53 -0
- package/src/ai/catalog/best-practices/error-handling-and-resiliency.md +116 -0
- package/src/ai/catalog/best-practices/genai-assistant-integration-patterns.md +49 -0
- package/src/ai/catalog/best-practices/performance-and-efficiency.md +109 -0
- package/src/ai/catalog/best-practices/security-considerations.md +93 -0
- package/src/ai/catalog/best-practices/testing-pconnect-components.md +245 -0
- package/src/ai/catalog/best-practices/troubleshooting-common-pitfalls.md +132 -0
- package/src/ai/catalog/pconnect-assignment-lifecycle/SKILL.md +44 -0
- package/src/ai/catalog/pconnect-assignment-lifecycle/cookbooks/README.md +39 -0
- package/src/ai/catalog/pconnect-assignment-lifecycle/cookbooks/approval-workflow-decision.md +47 -0
- package/src/ai/catalog/pconnect-assignment-lifecycle/cookbooks/attachment-upload.md +45 -0
- package/src/ai/catalog/pconnect-assignment-lifecycle/cookbooks/bulk-action-submit.md +50 -0
- package/src/ai/catalog/pconnect-assignment-lifecycle/cookbooks/confirm-before-destructive-action.md +43 -0
- package/src/ai/catalog/pconnect-assignment-lifecycle/cookbooks/create-and-stage-new-case.md +45 -0
- package/src/ai/catalog/pconnect-assignment-lifecycle/cookbooks/graceful-submit-failure.md +39 -0
- package/src/ai/catalog/pconnect-assignment-lifecycle/cookbooks/modal-assignment-submit.md +58 -0
- package/src/ai/catalog/pconnect-assignment-lifecycle/cookbooks/multi-step-wizard.md +44 -0
- package/src/ai/catalog/pconnect-assignment-lifecycle/cookbooks/resolve-or-ignore-duplicate.md +47 -0
- package/src/ai/catalog/pconnect-assignment-lifecycle/cookbooks/safe-case-deletion.md +36 -0
- package/src/ai/catalog/pconnect-case-collaboration/SKILL.md +42 -0
- package/src/ai/catalog/pconnect-case-collaboration/cookbooks/README.md +36 -0
- package/src/ai/catalog/pconnect-case-collaboration/cookbooks/look-up-user-details.md +43 -0
- package/src/ai/catalog/pconnect-case-collaboration/cookbooks/manage-case-attachments.md +51 -0
- package/src/ai/catalog/pconnect-case-collaboration/cookbooks/manage-case-participants.md +52 -0
- package/src/ai/catalog/pconnect-case-collaboration/cookbooks/participant-and-follower.md +40 -0
- package/src/ai/catalog/pconnect-case-collaboration/cookbooks/post-and-manage-feed.md +50 -0
- package/src/ai/catalog/pconnect-case-collaboration/cookbooks/relate-cases.md +35 -0
- package/src/ai/catalog/pconnect-case-collaboration/cookbooks/tag-case-and-find-related.md +44 -0
- package/src/ai/catalog/pconnect-data-subscriptions/SKILL.md +42 -0
- package/src/ai/catalog/pconnect-data-subscriptions/cookbooks/README.md +35 -0
- package/src/ai/catalog/pconnect-data-subscriptions/cookbooks/data-page-subscription-cleanup.md +41 -0
- package/src/ai/catalog/pconnect-data-subscriptions/cookbooks/publish-and-subscribe-events.md +40 -0
- package/src/ai/catalog/pconnect-data-subscriptions/cookbooks/pubsub-cleanup.md +38 -0
- package/src/ai/catalog/pconnect-data-subscriptions/cookbooks/query-paginated-list-data.md +44 -0
- package/src/ai/catalog/pconnect-data-subscriptions/cookbooks/realtime-case-subscription.md +46 -0
- package/src/ai/catalog/pconnect-data-subscriptions/cookbooks/refresh-and-update-data.md +41 -0
- package/src/ai/catalog/pconnect-forms-and-fields/SKILL.md +44 -0
- package/src/ai/catalog/pconnect-forms-and-fields/cookbooks/README.md +36 -0
- package/src/ai/catalog/pconnect-forms-and-fields/cookbooks/autosave-tracking.md +45 -0
- package/src/ai/catalog/pconnect-forms-and-fields/cookbooks/cascading-dependent-fields.md +51 -0
- package/src/ai/catalog/pconnect-forms-and-fields/cookbooks/collect-form-changes-for-manual-save.md +45 -0
- package/src/ai/catalog/pconnect-forms-and-fields/cookbooks/custom-field-validators.md +44 -0
- package/src/ai/catalog/pconnect-forms-and-fields/cookbooks/discard-changes-and-reset-form.md +41 -0
- package/src/ai/catalog/pconnect-forms-and-fields/cookbooks/full-form-validation.md +38 -0
- package/src/ai/catalog/pconnect-forms-and-fields/cookbooks/persist-widget-ui-state.md +41 -0
- package/src/ai/catalog/pconnect-lists-and-views/SKILL.md +44 -0
- package/src/ai/catalog/pconnect-lists-and-views/cookbooks/README.md +35 -0
- package/src/ai/catalog/pconnect-lists-and-views/cookbooks/activate-and-switch-container-items.md +52 -0
- package/src/ai/catalog/pconnect-lists-and-views/cookbooks/container-item-lifecycle.md +61 -0
- package/src/ai/catalog/pconnect-lists-and-views/cookbooks/insert-delete-list-rows.md +40 -0
- package/src/ai/catalog/pconnect-lists-and-views/cookbooks/reorder-list-rows.md +39 -0
- package/src/ai/catalog/pconnect-lists-and-views/cookbooks/repeating-row-edit.md +39 -0
- package/src/ai/catalog/pconnect-lists-and-views/cookbooks/view-personalization.md +60 -0
- package/src/ai/catalog/pconnect-navigation-locale-genai/SKILL.md +45 -0
- package/src/ai/catalog/pconnect-navigation-locale-genai/cookbooks/README.md +35 -0
- package/src/ai/catalog/pconnect-navigation-locale-genai/cookbooks/agent-conversation-for-tasks.md +54 -0
- package/src/ai/catalog/pconnect-navigation-locale-genai/cookbooks/bookmarkable-filter-state.md +40 -0
- package/src/ai/catalog/pconnect-navigation-locale-genai/cookbooks/deep-link-semantic-url.md +39 -0
- package/src/ai/catalog/pconnect-navigation-locale-genai/cookbooks/genai-assistant-conversation.md +43 -0
- package/src/ai/catalog/pconnect-navigation-locale-genai/cookbooks/locale-switch-and-relocalize.md +38 -0
- package/src/ai/catalog/pconnect-navigation-locale-genai/cookbooks/persist-operator-preferences.md +40 -0
- package/src/ai/catalog/references/architecture-overview.md +52 -0
- package/src/ai/catalog/references/pconnect-actions-api.md +948 -0
- package/src/ai/catalog/references/pconnect-case-info-data-info-localization.md +618 -0
- package/src/ai/catalog/references/pconnect-case-validation-utilities.md +238 -0
- package/src/ai/catalog/references/pconnect-container-manager.md +215 -0
- package/src/ai/catalog/references/pconnect-list-actions.md +250 -0
- package/src/ai/catalog/references/pconnect-misc-suggestions-creation.md +231 -0
- package/src/ai/catalog/references/pconnect-reference-context-component.md +471 -0
- package/src/ai/catalog/references/pconnect-state-config.md +316 -0
- package/src/ai/catalog/references/pconnect-validation-api.md +45 -0
- package/src/ai/catalog/references/pcore-analytics-genai-details.md +50 -0
- package/src/ai/catalog/references/pcore-bootstrap-init-details.md +121 -0
- package/src/ai/catalog/references/pcore-bootstrap-registry-routing.md +253 -0
- package/src/ai/catalog/references/pcore-case-collaboration-details.md +191 -0
- package/src/ai/catalog/references/pcore-case-collaboration-genai.md +264 -0
- package/src/ai/catalog/references/pcore-environment-info-details.md +96 -0
- package/src/ai/catalog/references/pcore-environment-locale-namespace.md +175 -0
- package/src/ai/catalog/references/pcore-messaging-details.md +99 -0
- package/src/ai/catalog/references/pcore-misc-details.md +207 -0
- package/src/ai/catalog/references/pcore-misc-utilities.md +325 -0
- package/src/ai/catalog/references/pcore-rest-data-details.md +164 -0
- package/src/ai/catalog/references/pcore-rest-data-messaging.md +308 -0
- package/src/ai/catalog/references/pcore-store-context-details.md +40 -0
- package/src/ai/catalog/references/pcore-store-context-state.md +130 -0
- package/src/ai/index.js +154 -0
- package/src/ai/project/.claude/skills/boolean/SKILL.md +43 -0
- package/src/ai/project/.claude/skills/case/SKILL.md +58 -0
- package/src/ai/project/.claude/skills/create-dx-component/SKILL.md +98 -0
- package/src/ai/project/.claude/skills/create-dx-component/assets/PConnProps.d.ts +24 -0
- package/src/ai/project/.claude/skills/create-dx-component/assets/create-nonce.ts +5 -0
- package/src/ai/project/.claude/skills/create-dx-component/assets/event-utils.ts +18 -0
- package/src/ai/project/.claude/skills/create-dx-component/assets/styles.ts +7 -0
- package/src/ai/project/.claude/skills/create-dx-component/cookbooks/README.md +28 -0
- package/src/ai/project/.claude/skills/create-dx-component/cookbooks/build-a-data-widget.md +281 -0
- package/src/ai/project/.claude/skills/create-dx-component/cookbooks/field-with-limits-and-validation.md +156 -0
- package/src/ai/project/.claude/skills/create-dx-component/cookbooks/status-badge-field.md +97 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/accessibility.md +71 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/choosing-type-and-subtype.md +79 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/cli-workflow.md +125 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/component-patterns.md +241 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/config-schema.md +296 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/cosmos-components.md +137 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/field-behavior.md +155 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/field-essentials.md +90 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/field-subtypes-extra.md +68 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/localization.md +30 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/pcore-pconnect-apis.md +77 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/presentation-essentials.md +57 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/props-contract.md +182 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/state-matrix.md +102 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/storybook-pattern.md +262 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/styles-pattern.md +54 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/template-essentials.md +87 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/template-patterns.md +131 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/test-pattern.md +215 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/ui-gallery-reuse.md +93 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/widget-essentials.md +84 -0
- package/src/ai/project/.claude/skills/create-dx-component/references/widget-patterns.md +164 -0
- package/src/ai/project/.claude/skills/currency/SKILL.md +46 -0
- package/src/ai/project/.claude/skills/date/SKILL.md +46 -0
- package/src/ai/project/.claude/skills/decimal/SKILL.md +49 -0
- package/src/ai/project/.claude/skills/details/SKILL.md +60 -0
- package/src/ai/project/.claude/skills/email/SKILL.md +42 -0
- package/src/ai/project/.claude/skills/field/SKILL.md +68 -0
- package/src/ai/project/.claude/skills/form/SKILL.md +51 -0
- package/src/ai/project/.claude/skills/generate-component-set/SKILL.md +80 -0
- package/src/ai/project/.claude/skills/generate-component-set/component-manifest.json +161 -0
- package/src/ai/project/.claude/skills/identify-components/SKILL.md +76 -0
- package/src/ai/project/.claude/skills/integer/SKILL.md +45 -0
- package/src/ai/project/.claude/skills/page/SKILL.md +60 -0
- package/src/ai/project/.claude/skills/page-widget/SKILL.md +55 -0
- package/src/ai/project/.claude/skills/percentage/SKILL.md +52 -0
- package/src/ai/project/.claude/skills/phone/SKILL.md +44 -0
- package/src/ai/project/.claude/skills/picklist/SKILL.md +50 -0
- package/src/ai/project/.claude/skills/plan-dx-component/SKILL.md +51 -0
- package/src/ai/project/.claude/skills/plan-dx-component/references/design-system-mapping.md +108 -0
- package/src/ai/project/.claude/skills/plan-dx-component/references/intake.md +146 -0
- package/src/ai/project/.claude/skills/plan-dx-component/references/plan-template.md +76 -0
- package/src/ai/project/.claude/skills/plan-dx-component/references/requirements-template.md +88 -0
- package/src/ai/project/.claude/skills/presentation/SKILL.md +61 -0
- package/src/ai/project/.claude/skills/publish-library/SKILL.md +106 -0
- package/src/ai/project/.claude/skills/radiobuttons/SKILL.md +51 -0
- package/src/ai/project/.claude/skills/review-component/SKILL.md +49 -0
- package/src/ai/project/.claude/skills/review-component/references/checklist.md +77 -0
- package/src/ai/project/.claude/skills/template/SKILL.md +60 -0
- package/src/ai/project/.claude/skills/text/SKILL.md +45 -0
- package/src/ai/project/.claude/skills/update-dx-component/SKILL.md +56 -0
- package/src/ai/project/.claude/skills/update-dx-component/references/maintenance.md +43 -0
- package/src/ai/project/.claude/skills/url/SKILL.md +46 -0
- package/src/ai/project/.claude/skills/validate-component/SKILL.md +78 -0
- package/src/ai/project/.claude/skills/validate-component/references/runtime-troubleshooting.md +56 -0
- package/src/ai/project/.claude/skills/validate-component/scripts/check-component.mjs +327 -0
- package/src/ai/project/.claude/skills/widget/SKILL.md +59 -0
- package/src/ai/project/.gemini/settings.json +5 -0
- package/src/ai/project/AGENTS.md +74 -0
- package/src/ai/readme-section.md +91 -0
- package/src/cli.js +48 -7
- package/src/constants.js +54 -53
- package/src/internal-tests.js +27 -0
- package/src/tasks/ai-toggle/index.js +46 -0
- package/src/tasks/authenticate/helper.js +38 -38
- package/src/tasks/authenticate/index.js +17 -15
- package/src/tasks/build-all/helper.js +4 -4
- package/src/tasks/build-all/index.js +7 -7
- package/src/tasks/build-comp/helper.js +17 -17
- package/src/tasks/build-comp/index.js +10 -10
- package/src/tasks/build-jest-assets/index.js +7 -9
- package/src/tasks/build-lib/helper.js +8 -8
- package/src/tasks/build-lib/index.js +8 -8
- package/src/tasks/bundle/builder/index.js +9 -12
- package/src/tasks/bundle/builder/modules.js +8 -9
- package/src/tasks/bundle/builder/paths.js +2 -19
- package/src/tasks/bundle/builder/webpack.config.js +60 -77
- package/src/tasks/bundle/index.js +8 -8
- package/src/tasks/create/index.js +35 -35
- package/src/tasks/create/templates/Constellation/Field/Boolean/index.tsx.mustache +1 -36
- package/src/tasks/create/templates/Constellation/Field/Date/date.ts.mustache +2 -2
- package/src/tasks/create/templates/Constellation/Field/Date/index.tsx.mustache +2 -27
- package/src/tasks/create/templates/Constellation/Field/DateTime/date-utils.ts.mustache +2 -2
- package/src/tasks/create/templates/Constellation/Field/DateTime/date.ts.mustache +2 -2
- package/src/tasks/create/templates/Constellation/Field/DateTime/index.tsx.mustache +2 -35
- package/src/tasks/create/templates/Constellation/Field/Decimal/index.tsx.mustache +3 -36
- package/src/tasks/create/templates/Constellation/Field/Decimal-Currency/index.tsx.mustache +4 -39
- package/src/tasks/create/templates/Constellation/Field/Decimal-Percentage/index.tsx.mustache +4 -53
- package/src/tasks/create/templates/Constellation/Field/Icon-Button-URL/index.tsx.mustache +5 -5
- package/src/tasks/create/templates/Constellation/Field/Integer/index.tsx.mustache +3 -36
- package/src/tasks/create/templates/Constellation/Field/Picklist/index.tsx.mustache +7 -42
- package/src/tasks/create/templates/Constellation/Field/Picklist-Autocomplete/component-constants.ts +1 -1
- package/src/tasks/create/templates/Constellation/Field/Picklist-Autocomplete/date.ts.mustache +0 -2
- package/src/tasks/create/templates/Constellation/Field/Picklist-Autocomplete/embed-utils.ts.mustache +1 -1
- package/src/tasks/create/templates/Constellation/Field/Picklist-Autocomplete/index.tsx.mustache +11 -11
- package/src/tasks/create/templates/Constellation/Field/Picklist-Autocomplete/repeat-utils.ts.mustache +3 -13
- package/src/tasks/create/templates/Constellation/Field/Picklist-Autocomplete/textformat-utils.tsx.mustache +2 -3
- package/src/tasks/create/templates/Constellation/Field/Picklist-Autocomplete/utils.ts.mustache +28 -28
- package/src/tasks/create/templates/Constellation/Field/Picklist-RadioButtons/index.tsx.mustache +2 -2
- package/src/tasks/create/templates/Constellation/Field/Picklist-RadioButtons/textformat-utils.tsx.mustache +2 -3
- package/src/tasks/create/templates/Constellation/Field/Text/index.tsx.mustache +4 -33
- package/src/tasks/create/templates/Constellation/Field/Text-Email/index.tsx.mustache +3 -33
- package/src/tasks/create/templates/Constellation/Field/Text-Input/index.tsx.mustache +3 -31
- package/src/tasks/create/templates/Constellation/Field/Text-Paragraph/index.tsx.mustache +3 -31
- package/src/tasks/create/templates/Constellation/Field/Text-Phone/index.tsx.mustache +3 -35
- package/src/tasks/create/templates/Constellation/Field/Text-URL/index.tsx.mustache +3 -33
- package/src/tasks/create/templates/Constellation/Field/Text-URL/text-url.tsx.mustache +2 -3
- package/src/tasks/create/templates/Constellation/Field/TimeofDay/index.tsx.mustache +2 -35
- package/src/tasks/create/templates/Constellation/Field/TimeofDay/time-of-day.ts.mustache +2 -2
- package/src/tasks/create/templates/Constellation/Template/DETAILS/Avatar.tsx.mustache +19 -15
- package/src/tasks/create/templates/Constellation/Template/DETAILS/Operator.tsx.mustache +6 -6
- package/src/tasks/create/templates/Constellation/Template/DETAILS/demo.stories.tsx.mustache +1 -1
- package/src/tasks/create/templates/Constellation/Template/DETAILS/index.tsx.mustache +4 -8
- package/src/tasks/create/templates/Constellation/Template/DETAILS/mock.ts.mustache +9 -9
- package/src/tasks/create/templates/Constellation/Template/DETAILS/utils.js.mustache +4 -8
- package/src/tasks/create/templates/Constellation/Template/DETAILS-region/Avatar.tsx.mustache +19 -15
- package/src/tasks/create/templates/Constellation/Template/DETAILS-region/Operator.tsx.mustache +6 -6
- package/src/tasks/create/templates/Constellation/Template/DETAILS-region/demo.stories.tsx.mustache +1 -1
- package/src/tasks/create/templates/Constellation/Template/DETAILS-region/index.tsx.mustache +1 -1
- package/src/tasks/create/templates/Constellation/Template/DETAILS-region/mock.ts.mustache +9 -9
- package/src/tasks/create/templates/Constellation/Template/DETAILS-region/utils.ts.mustache +4 -8
- package/src/tasks/create/templates/Constellation/Template/FORM/demo.stories.tsx.mustache +8 -8
- package/src/tasks/create/templates/Constellation/Template/FORM-region/demo.stories.tsx.mustache +8 -8
- package/src/tasks/create/templates/Constellation/Template/PAGE/utils.ts.mustache +4 -9
- package/src/tasks/create/templates/Constellation/Widget/CASE/demo.test.tsx.mustache +3 -3
- package/src/tasks/create/templates/Constellation/Widget/PAGE-CASE/Avatar.tsx.mustache +19 -14
- package/src/tasks/create/templates/Constellation/Widget/PAGE-CASE/Operator.tsx.mustache +6 -6
- package/src/tasks/create/templates/Constellation/Widget/PAGE-CASE/index.tsx.mustache +1 -1
- package/src/tasks/create/templates/Constellation/Widget/PAGE-CASE/mock.ts.mustache +1 -1
- package/src/tasks/create/templates/Launchpad/Field/Icon-Button-URL/index.tsx.mustache +5 -5
- package/src/tasks/create/templates/Launchpad/Field/Text-Phone/index.tsx.mustache +3 -35
- package/src/tasks/create/templates/Launchpad/Field/__components.md +1 -1
- package/src/tasks/create/templates/Launchpad/Template/FORM/demo.stories.tsx.mustache +8 -8
- package/src/tasks/create/templates/Launchpad/Template/FORM-region/demo.stories.tsx.mustache +8 -8
- package/src/tasks/create/templates/Launchpad/Widget/CASE/demo.test.tsx.mustache +3 -3
- package/src/tasks/create/templates/Launchpad/Widget/PAGE-CASE/index.tsx.mustache +1 -1
- package/src/tasks/create/templates/Launchpad/Widget/PAGE-CASE/mock.ts.mustache +1 -1
- package/src/tasks/create-all/index.js +42 -41
- package/src/tasks/create-lib/helper.js +13 -13
- package/src/tasks/create-lib/index.js +5 -5
- package/src/tasks/create-version/helper.js +10 -10
- package/src/tasks/create-version/index.js +3 -4
- package/src/tasks/delete/helper.js +20 -19
- package/src/tasks/delete/index.js +2 -2
- package/src/tasks/delete-all/helper.js +26 -25
- package/src/tasks/delete-all/index.js +2 -2
- package/src/tasks/delete-lib/helper.js +6 -6
- package/src/tasks/delete-lib/index.js +6 -6
- package/src/tasks/delete-pub/helper.js +18 -18
- package/src/tasks/delete-pub/index.js +8 -8
- package/src/tasks/delete-version/index.js +2 -2
- package/src/tasks/dev-server/index.js +1 -1
- package/src/tasks/export-archive/helper.js +21 -21
- package/src/tasks/export-archive/index.js +27 -40
- package/src/tasks/export-rc/helper.js +34 -34
- package/src/tasks/export-rc/index.js +21 -31
- package/src/tasks/import-archive/helper.js +39 -39
- package/src/tasks/import-archive/index.js +69 -89
- package/src/tasks/import-comp/helper.js +8 -8
- package/src/tasks/import-comp/index.js +30 -42
- package/src/tasks/index.js +5 -0
- package/src/tasks/init/Node.gitignore +149 -0
- package/src/tasks/init/helper.js +15 -8
- package/src/tasks/init/index.js +115 -35
- package/src/tasks/init/template/.prettierrc.json +4 -1
- package/src/tasks/init/template/.storybook/main.ts +1 -4
- package/src/tasks/init/template/.storybook/manager.ts +1 -1
- package/src/tasks/init/template/.storybook/preview.tsx +2 -2
- package/src/tasks/init/template/.storybook/theme.js +1 -1
- package/src/tasks/init/template/.vscode/extensions.json +0 -1
- package/src/tasks/init/template/README.md +2 -2
- package/src/tasks/init/template/eslint.config.mjs +13 -155
- package/src/tasks/init/template/jest.config.cjs +2 -0
- package/src/tasks/init/template/package-lock.json +26864 -0
- package/src/tasks/init/template/package.json.mustache +19 -74
- package/src/tasks/init/template/setupTests.ts +10 -4
- package/src/tasks/init/template/tsconfig.json +14 -6
- package/src/tasks/internal-test/index.js +21 -0
- package/src/tasks/internal-test-full/index.js +15 -0
- package/src/tasks/internal-test-functional/index.js +15 -0
- package/src/tasks/internal-test-unit/index.js +13 -0
- package/src/tasks/linter/index.js +2 -2
- package/src/tasks/list/helper.js +41 -40
- package/src/tasks/list/index.js +3 -3
- package/src/tasks/list-lib/helper.js +5 -5
- package/src/tasks/list-lib/index.js +8 -8
- package/src/tasks/list-pub/helper.js +23 -16
- package/src/tasks/list-pub/index.js +24 -21
- package/src/tasks/move/helper.js +8 -8
- package/src/tasks/move/index.js +19 -23
- package/src/tasks/promote-webpack/index.js +4 -4
- package/src/tasks/promote-webpack/webpack.config.js +62 -93
- package/src/tasks/publish/helper.js +21 -21
- package/src/tasks/publish/index.js +16 -16
- package/src/tasks/publish-all/helper.js +20 -20
- package/src/tasks/publish-all/index.js +11 -12
- package/src/tasks/publish-archive/helper.js +9 -9
- package/src/tasks/publish-archive/index.js +12 -14
- package/src/tasks/publish-lib/helper.js +3 -3
- package/src/tasks/publish-lib/index.js +28 -34
- package/src/tasks/quarantine/index.js +8 -8
- package/src/tasks/rename/index.js +26 -30
- package/src/tasks/rename-lib/index.js +21 -25
- package/src/tasks/restore/index.js +8 -8
- package/src/tasks/restore-config/index.js +3 -5
- package/src/tasks/set-perm/index.js +8 -8
- package/src/tasks/show-help/index.js +17 -9
- package/src/tasks/show-status/index.js +2 -2
- package/src/tasks/store/index.js +8 -8
- package/src/tasks/switch-lib/helper.js +7 -7
- package/src/tasks/switch-lib/index.js +7 -7
- package/src/tasks/switch-version/helper.js +6 -6
- package/src/tasks/switch-version/index.js +7 -7
- package/src/tasks/update/index.js +1 -1
- package/src/tasks/upgrade/index.js +7 -7
- package/src/tasks/validator/index.js +49 -203
- package/src/util.js +169 -186
- package/src/rules/no-pcore-pconnect-in-presentation.js +0 -103
- package/src/tasks/init/template/.stylelintrc.json +0 -4
- package/src/tasks/init/template/babel.config.cjs +0 -22
- package/src/tasks/init/template/cspell.json +0 -14
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: update-dx-component
|
|
3
|
+
description: 'Change an EXISTING Pega DX component safely: add or change a behavior, add or remove an authoring property, fix a bug, restyle, change a stored-value format, or plan a rename, move, version bump or upgrade. Use when: the user asks to modify, extend, fix, refactor, rename or retire a component that already exists under src/components. For a brand-new component use the type skills instead.'
|
|
4
|
+
argument-hint: 'Name the component and the change (for example "Acme_Lib_Quantity: add a step property")'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Update an Existing DX Component
|
|
8
|
+
|
|
9
|
+
A component that has been published may already be configured in views that people use. A change that looks small in code can break those views or the data they saved, so this skill is about understanding what exists, classifying the risk, and keeping every artifact in step.
|
|
10
|
+
|
|
11
|
+
## Procedure
|
|
12
|
+
|
|
13
|
+
### 1. Understand what exists
|
|
14
|
+
|
|
15
|
+
- Read the whole folder `src/components/<Org>_<Library>_<Name>/`: `config.json`, `index.tsx` and helpers, `styles.ts`, `mock.ts`, `demo.stories.tsx`, `demo.test.tsx`, `localizations.json` if present.
|
|
16
|
+
- Write down the current contract in a few lines: the type and subtype, the authoring properties and their defaults, the value or data it handles, the states it covers. If a brief or the original request is available, compare it with the code and note any difference.
|
|
17
|
+
- Run `node .claude/skills/validate-component/scripts/check-component.mjs <key> --no-build` first. This is the baseline. Failures that were already there are not yours to hide or to fix unasked; report them.
|
|
18
|
+
|
|
19
|
+
### 2. Classify the change
|
|
20
|
+
|
|
21
|
+
Use the lists in the "Safe and risky changes" section of [config-schema.md](../create-dx-component/references/config-schema.md).
|
|
22
|
+
|
|
23
|
+
| Class | Examples | What to do |
|
|
24
|
+
| --- | --- | --- |
|
|
25
|
+
| Safe | A bug fix with no contract change; styling; an optional property with a backward-compatible default; a new state; better tests | Make it, keeping all artifacts in sync |
|
|
26
|
+
| Behavior change | Different validation, a different commit moment, new display rules | Tell the user what existing users will see change, then make it |
|
|
27
|
+
| Risky | Renaming or removing a property, region or option key; changing the type or shape of a stored value; changing a default so existing components behave differently; changing a picker from scalars to objects; hiding a property that was editable | Stop. Describe the effect on views and data already saved and propose a compatible alternative (add the new shape, keep the old one). Proceed only when the user chooses |
|
|
28
|
+
| Different component | A different type or subtype; a different stored value type | The subtype cannot change in place. Propose a new component and retiring the old one |
|
|
29
|
+
| Structural | Rename, move, copy, delete, switch library or version | These are the user's commands. See [maintenance.md](./references/maintenance.md); do not run them unasked |
|
|
30
|
+
|
|
31
|
+
When in doubt, treat the change as risky and ask once.
|
|
32
|
+
|
|
33
|
+
### 3. Plan before editing
|
|
34
|
+
|
|
35
|
+
State, in one message, what will change in each artifact, the class from step 2, how absent and legacy values behave (what a view saved before the change will do), and what the user must do afterwards (publish, retest). Wait for approval for anything above Safe.
|
|
36
|
+
|
|
37
|
+
### 4. Make the change
|
|
38
|
+
|
|
39
|
+
- Change `config.json`, `index.tsx`, `styles.ts`, `mock.ts`, the stories and the tests **together**. Every property in `config.json` must still be read by the code, with a safe default for an absent value.
|
|
40
|
+
- Keep the diff small. Do not refactor unrelated code, rename scaffold-owned names, or upgrade dependencies as a side effect.
|
|
41
|
+
- Follow the same rules as for a new component: Cosmos first, no inline styles, no new third-party library without consent, only documented PCore and PConnect methods. Read the references that match the type: [field-behavior.md](../create-dx-component/references/field-behavior.md), [widget-patterns.md](../create-dx-component/references/widget-patterns.md) or [template-patterns.md](../create-dx-component/references/template-patterns.md).
|
|
42
|
+
- Do not delete or weaken an existing test to make a change pass. If an intentional change makes a test wrong, update it and say why.
|
|
43
|
+
|
|
44
|
+
### 5. Prove it
|
|
45
|
+
|
|
46
|
+
- Add tests for the new behavior, and one test for the legacy case (an absent or old value renders as before).
|
|
47
|
+
- Run `node .claude/skills/validate-component/scripts/check-component.mjs <key>` and paste the table into the report, as for a new component.
|
|
48
|
+
- For anything above Safe, run the [review-component](../review-component/SKILL.md) skill.
|
|
49
|
+
|
|
50
|
+
### 6. Report
|
|
51
|
+
|
|
52
|
+
Say what changed in each file, the class of the change, who or what is affected (views already using the component, saved data), and what you could not verify. Remind the user that the change reaches Pega only after they publish (a user-run step) and that it should be retested in App Studio.
|
|
53
|
+
|
|
54
|
+
## Version numbers
|
|
55
|
+
|
|
56
|
+
The `version` in `config.json` is a label for this component. Whether to bump it, and whether to create a new library version, is the user's decision: ask, and explain that a published library version is what Pega loads. Do not run `createLibVersion`, `publish` or any other server or structural command unasked.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Maintenance, Renames, Versions and Upgrades
|
|
2
|
+
|
|
3
|
+
What a customer owns after the first publish, and the commands that exist for it. Every command here is the user's to run unless they explicitly ask you to; they need credentials, a server, or change or delete data. The exact forms are in [cli-workflow.md](../../create-dx-component/references/cli-workflow.md) and [publish-library](../../publish-library/SKILL.md).
|
|
4
|
+
|
|
5
|
+
## What the customer owns
|
|
6
|
+
|
|
7
|
+
The CLI prints this on its help screen, and it should be said plainly to anyone building custom components: by choosing to create and use custom components, you agree to keep them up to date with every release of Pega Infinity, and to meet compliance standards for data privacy and accessibility. Pega does not update a custom component for you.
|
|
8
|
+
|
|
9
|
+
## Rename, move and delete
|
|
10
|
+
|
|
11
|
+
| Goal | Command | Effect and caution |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| Rename a component | `npm run rename` | Changes the component key, which is the component's persisted identity. A view that references the old key is likely to stop finding it, so treat a rename of a published component as a breaking change and ask the user first. Prefer a new component with the new name and retire the old one later |
|
|
14
|
+
| Move or copy a component (or all) to another version or library | `npm run move` | Check the target library and version with the user first |
|
|
15
|
+
| Delete a component | `npm run delete` | Destructive. Confirm which one, and that no view still uses it |
|
|
16
|
+
| Delete everything | `npm run deleteAll`, `npm run deleteLib`, `npm run deleteLibVersion`, `npm run deletePublishedLib` | Destructive and often permanent. Never run unasked |
|
|
17
|
+
|
|
18
|
+
## Library versions
|
|
19
|
+
|
|
20
|
+
| Goal | Command | Note |
|
|
21
|
+
| --- | --- | --- |
|
|
22
|
+
| See the current library, version and component count | `npm run showStatus` | Safe |
|
|
23
|
+
| List the contents of a library or version | `npm run listLib`, `npm run list` | `listLib` prompts and cannot run without a terminal |
|
|
24
|
+
| Start a new version | `npm run createLibVersion` | Ask the user first |
|
|
25
|
+
| Switch version or library | `npm run switchLibVersion`, `npm run switchLib` | Changes which version later commands act on |
|
|
26
|
+
| Save a version locally | `npm run storeLibVersion` | Save components before storing |
|
|
27
|
+
| Go back to the stored copy | `npm run restoreLibVersion` | The CLI warns that current changes will be **lost**. Never run unasked |
|
|
28
|
+
| Export or import a version as a zip | `npm run exportLibVersion`, `npm run importLibVersion` | |
|
|
29
|
+
| Publish | `npm run publish` | See [publish-library](../../publish-library/SKILL.md) |
|
|
30
|
+
|
|
31
|
+
## Upgrading
|
|
32
|
+
|
|
33
|
+
- **The CLI.** `npm run upgrade` reinstalls `@pega/custom-dx-components` at the latest version. It changes the project's dependencies, so ask first, then run the check script for every component and `npm run validateAll`.
|
|
34
|
+
- **Pega Infinity.** After the customer's Infinity version changes, republish and retest the components in App Studio. Check the Pega documentation for the new release for anything that affects custom components, and tell the user what you could not verify.
|
|
35
|
+
- **Cosmos.** Components are built against the installed `@pega/cosmos-react-core`. When it changes, a prop can be added, renamed or removed. After any dependency change run `npx tsc --noEmit`, the check script and the Storybook states, and read the `.d.ts` of any Cosmos component that now fails to compile instead of guessing the new prop.
|
|
36
|
+
- **AI skills.** `npm run enableAi` restores missing skill files but never overwrites an existing one, so installed skills do not update themselves. If the guidance feels out of date, ask the user to compare `.claude/skills` with the version that ships in the installed CLI package.
|
|
37
|
+
|
|
38
|
+
## Before any structural change
|
|
39
|
+
|
|
40
|
+
1. Run `npm run showStatus` and `npm run list` to see the current library, version and components.
|
|
41
|
+
2. Say exactly what the command will change and what cannot be undone.
|
|
42
|
+
3. Make sure local work is saved, for example with `npm run storeLibVersion`, if the command can lose it.
|
|
43
|
+
4. Let the user run the command, or run it only when they ask.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: url
|
|
3
|
+
description: 'Scaffold a Pega DX Field component with subtype **URL** — web address input bound to a string case property. Use when: user asks for a URL input, website link field, external reference, or a clickable link display.'
|
|
4
|
+
argument-hint: 'Describe the URL field (e.g., "a company website input that opens in a new tab")'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Create URL Field
|
|
8
|
+
|
|
9
|
+
Field subtype `URL`. Binds to a string case property holding a web address.
|
|
10
|
+
|
|
11
|
+
Read [../field/SKILL.md](../field/SKILL.md) for the shared Field rules first. This skill adds only what is specific to `URL`.
|
|
12
|
+
|
|
13
|
+
## Cosmos Mapping
|
|
14
|
+
|
|
15
|
+
| Mode | Component |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| Edit | `Input` with `type='url'` from `@pega/cosmos-react-core` |
|
|
18
|
+
| `DISPLAY_ONLY` | `URLDisplay` |
|
|
19
|
+
|
|
20
|
+
## Value Handling
|
|
21
|
+
|
|
22
|
+
- Trim on blur and prepend `https://` when the user omitted a scheme, then dispatch the normalized value.
|
|
23
|
+
- Validate the scheme before storing. **Only `http:` and `https:` are acceptable.**
|
|
24
|
+
Parse the value with `new URL(raw)` inside a try/catch, and accept it only when the protocol is `http:` or `https:`. A value that does not parse is unsafe. The helper shape is in [field-behavior.md](../create-dx-component/references/field-behavior.md).
|
|
25
|
+
|
|
26
|
+
## config.json Additions
|
|
27
|
+
|
|
28
|
+
| Property | Label | Format | Default | Notes |
|
|
29
|
+
| --- | --- | --- | --- | --- |
|
|
30
|
+
| `openInNewTab` | Open in a new tab | `BOOLEAN` | `true` | |
|
|
31
|
+
| `linkText` | Link text override | `TEXT` | | |
|
|
32
|
+
|
|
33
|
+
## Security
|
|
34
|
+
|
|
35
|
+
This is the highest-risk Field subtype in the set.
|
|
36
|
+
|
|
37
|
+
- **Reject `javascript:`, `data:`, `vbscript:`, and `file:` schemes.** Rendering an unchecked user-supplied URL in an `href` is a stored XSS vector.
|
|
38
|
+
- Do not assume `URLDisplay` makes a value safe. In the Cosmos build checked, its `Link` only drops hrefs that match `javascript:`; `data:`, `vbscript:` and `file:` pass through, and it sets no `rel` on the anchor. Run the allow-list check above on the value before rendering, and verify the behavior against the installed Cosmos version.
|
|
39
|
+
- When `openInNewTab` is on, set `rel='noopener noreferrer'` yourself unless you have confirmed the rendered anchor already has it.
|
|
40
|
+
- Cosmos exports `disallowedURI` (a regex) in some builds; if it exists in the installed version you may use it as an extra check, but keep the explicit protocol allow-list.
|
|
41
|
+
- Never interpolate the value into `dangerouslySetInnerHTML`.
|
|
42
|
+
- Production URL fields commit the raw string on blur and do no scheme check, so the scheme rule above is **your** responsibility, not the platform's.
|
|
43
|
+
|
|
44
|
+
## Validate
|
|
45
|
+
|
|
46
|
+
Run `node .claude/skills/validate-component/scripts/check-component.mjs <Org>_<Library>_<Name>` and paste its table into your final report.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: validate-component
|
|
3
|
+
description: 'Build, validate, test, lint, and publish workflow for Pega DX components in this repo. Use for running npm run validate, diagnosing build or Storybook failures, fixing Jest coverage gaps, resolving lint and spelling errors, and publishing a component or library version.'
|
|
4
|
+
argument-hint: 'validate, test, lint, storybook, fix, or publish'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Validate Component Workflow
|
|
8
|
+
|
|
9
|
+
Use this skill when the task is about the build and release loop for an existing component, rather than authoring a new one. For authoring, use the `create-*-component` skills.
|
|
10
|
+
|
|
11
|
+
## One-command check (use this first)
|
|
12
|
+
|
|
13
|
+
Run `node .claude/skills/validate-component/scripts/check-component.mjs <Org>_<Library>_<Name>` from the project root. It runs the schema check, `tsc` (scoped to this component and incremental), ESLint and Jest in parallel, checks that every `config.json` property is actually read by the code, warns about formats the CLI scaffolds do not use, flags inline styles, raw HTML injection and secrets, warns when there is only one story or one test, and runs the build once the schema and ESLint checks pass. It prints a table of PASS, FAIL, WARN and NOT RUN, and exits 1 if anything failed. While fixing failures, add `--no-build` to skip the slow bundle; run it once without the flag for the final table.
|
|
14
|
+
|
|
15
|
+
Paste the table into your final report. Report a check as passing only when the table says PASS; a `NOT RUN` or `FAIL` row must be stated as such, with the reason. Use the individual commands below to iterate on a failure, then rerun the script for the final table. Do not weaken a check to turn a row green.
|
|
16
|
+
|
|
17
|
+
## Commands
|
|
18
|
+
|
|
19
|
+
| Command | Purpose |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| `npm run validate -- <Org>_<Library>_<Name> N N` | Validate the schema, lint and bundle one component (`custom-dx-components buildComponent <key> <sourceMap> <devBuild>`). **Without arguments it prompts interactively and hangs an agent** |
|
|
22
|
+
| `npm run validate-schema -- <Org>_<Library>_<Name>` | Check only `config.json` against the schema |
|
|
23
|
+
| `npm run validateAll` | Build all components (no prompts) |
|
|
24
|
+
| `npx tsc --noEmit 2>&1 \| grep -v demo.stories.tsx` | Type-check your own files; scaffolded stories report known `window.PCore` noise |
|
|
25
|
+
| `npx jest <folder> --coverage=false` | Run one component's tests without the coverage gate |
|
|
26
|
+
| `npm run test` | Run Jest with coverage |
|
|
27
|
+
| `npm run coverage` | Run Jest and emit an HTML coverage report |
|
|
28
|
+
| `npm run lint` | Run ESLint |
|
|
29
|
+
| `npm run _lint:types` | Type-check with `tsc --noEmit` |
|
|
30
|
+
| `npm run _lint:format` | Prettier check |
|
|
31
|
+
| `npm run _lint:spelling` | cspell check |
|
|
32
|
+
| `npm run _lint:styles` | Stylelint check |
|
|
33
|
+
| `npm run startStorybook` | Storybook dev server on port 6040 |
|
|
34
|
+
| `npm run publish` | Publish to the configured Pega server |
|
|
35
|
+
| `npm run authenticate` | Refresh server credentials before publishing |
|
|
36
|
+
|
|
37
|
+
## Coverage Thresholds
|
|
38
|
+
|
|
39
|
+
From `jest.config.cjs`, the global gate is statements 79, branches 74, functions 81, lines 80. A component that drops the global number below these fails the run even when its own tests pass.
|
|
40
|
+
|
|
41
|
+
## Procedure
|
|
42
|
+
|
|
43
|
+
1. Identify the component folder under `src/components/<Org>_<Library>_<Name>/`; the folder name is the component key.
|
|
44
|
+
2. Run `npm run validate -- <Org>_<Library>_<Name> N N` first (or `npm run validate-schema -- <Org>_<Library>_<Name>` for the schema alone). It surfaces schema and build errors faster than the test suite. The full check loop is in [../create-dx-component/references/cli-workflow.md](../create-dx-component/references/cli-workflow.md).
|
|
45
|
+
3. If `config.json` fails, check it against the schema reference in [../create-dx-component/references/config-schema.md](../create-dx-component/references/config-schema.md). Property `format` values must come from the supported list.
|
|
46
|
+
4. Run `npm run test`. If coverage fails, add cases for the untested branches rather than lowering the threshold.
|
|
47
|
+
5. Run `npm run lint` and `npm run _lint:types`. Fix unused imports, unused state, and unused interface fields, since dead code is a policy violation in this repo.
|
|
48
|
+
6. Run `npm run startStorybook` for a visual check when the change affects rendering.
|
|
49
|
+
7. Publishing is out of scope for this skill — use [../publish-library/SKILL.md](../publish-library/SKILL.md).
|
|
50
|
+
|
|
51
|
+
## After the build is green
|
|
52
|
+
|
|
53
|
+
If the component builds but misbehaves in App Studio or a real view (not offered in the picker, wrong property panel, blank render, differs from Storybook), use [runtime-troubleshooting.md](./references/runtime-troubleshooting.md). It is a diagnostic ladder that asks the user for evidence at each step.
|
|
54
|
+
|
|
55
|
+
## Common Failures
|
|
56
|
+
|
|
57
|
+
- **Missing `create-nonce` import** — styled-components fail CSP in the Pega host. Every component that uses `styled-components` must side-effect import `../shared/create-nonce`.
|
|
58
|
+
- **Missing `withConfiguration()`** — the component will not register with Constellation.
|
|
59
|
+
- **`PCore is not defined` in tests** — the Storybook file must assign `window.PCore` before the story renders.
|
|
60
|
+
- **Story not composing** — `demo.test.tsx` uses `composeStories`, so the export name in `demo.stories.tsx` must match the destructured name.
|
|
61
|
+
- **Type errors on `PConnProps`** — confirm the component folder has its own `PConnProps.d.ts` copied from the shared asset.
|
|
62
|
+
- **cspell failures** — add genuine domain terms to `cspell.json` rather than renaming identifiers.
|
|
63
|
+
- **`Highlight is not defined` / `X.animate is not a function` from inside `node_modules`** — jsdom is missing a browser API that Cosmos calls at module scope. Add the stub to [../../../setupTests.ts](../../../setupTests.ts); do not work around it in the component.
|
|
64
|
+
- **`messages.flatMap is not a function`** — `Banner` `messages` takes an array, not a string.
|
|
65
|
+
- **A Cosmos prop appears to do nothing** — it is probably not a real prop. Read `node_modules/@pega/cosmos-react-core/lib/components/<Name>/<Name>.d.ts`; `EmptyState` uses `message` not `title`, and `Tag` uses `children` not `label`.
|
|
66
|
+
- **`Cannot read properties of undefined (reading 'palette')` in `styles.ts`** — Cosmos ships a nested `styled-components`, so its theme never reaches your `styles.ts`. Call `useTheme()` in `index.tsx` and pass the values as props.
|
|
67
|
+
- **`npm run validate` prompts for input, or says "Please create/import a library"** — pass the component key and `N N` (`npm run validate -- <Org>_<Library>_<Name> N N`); if a library is missing, run `npm run createLib -- <Library> 1.0.0 Y Current` first (see [../publish-library/SKILL.md](../publish-library/SKILL.md)).
|
|
68
|
+
- **Coverage prints `0%`** — expected. `collectCoverageFrom` in [../../../jest.config.cjs](../../../jest.config.cjs) is scoped to the CLI's own source, so component code is never instrumented. Do not chase the number; still cover every branch you wrote.
|
|
69
|
+
|
|
70
|
+
## Output Format
|
|
71
|
+
|
|
72
|
+
When responding, prefer this order:
|
|
73
|
+
|
|
74
|
+
1. What was run and the result.
|
|
75
|
+
2. The specific failure, quoted from the output.
|
|
76
|
+
3. The root cause.
|
|
77
|
+
4. The fix applied, or the exact fix to apply.
|
|
78
|
+
5. The follow-up command to confirm the fix.
|
package/src/ai/project/.claude/skills/validate-component/references/runtime-troubleshooting.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Troubleshooting a Component After It Builds
|
|
2
|
+
|
|
3
|
+
For problems that appear after `check-component` is green: the component is not offered in App Studio, its property panel is wrong, it renders blank or wrongly in a real view, or it behaves differently from Storybook. Work down the ladder, **ask the user for the evidence in each rung instead of guessing**, and say what you could not check. You usually cannot see the Pega server, so never claim that something works or fails there without evidence from the user.
|
|
4
|
+
|
|
5
|
+
## 0. Get the facts first
|
|
6
|
+
|
|
7
|
+
Ask for, or look up:
|
|
8
|
+
|
|
9
|
+
- The exact symptom and where it appears (App Studio picker, property panel, a case view, a portal page).
|
|
10
|
+
- The text of any error in the browser console, and what the page shows instead (nothing, a generic failure message, the old version).
|
|
11
|
+
- `npm run showStatus` output (library, version, permanence, component count) and whether the component was published, and to which server and ruleset.
|
|
12
|
+
- The Pega Infinity version and the `@pega/cosmos-react-core` version in `package.json`.
|
|
13
|
+
- Whether Storybook shows the component correctly in the same state.
|
|
14
|
+
|
|
15
|
+
## 1. It is not offered in App Studio
|
|
16
|
+
|
|
17
|
+
| Check | How |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| It was published, from the right library and version | `npm run showStatus` and `npm run listPublishedLib` (local or server). Publishing is the user's step |
|
|
20
|
+
| The `type` and `subtype` suit the placement | A Field appears where fields are picked, a Template where layouts are picked, a Widget in a case view (`CASE`) or on a page (`PAGE`). A wrong subtype is a different placement, not an error. Compare against [choosing-type-and-subtype.md](../../create-dx-component/references/choosing-type-and-subtype.md) |
|
|
21
|
+
| The component key matches what was published | The key is `<Org>_<Library>_<Name>` and must agree in `config.json`, the folder and `tasks.config.json` (library mode enforces library and organization) |
|
|
22
|
+
| The user is looking in the right application and ruleset | These are server settings in `tasks.config.json` (`rulesetName`, `rulesetVersion`). Ask; do not change them |
|
|
23
|
+
|
|
24
|
+
## 2. The property panel is wrong or empty
|
|
25
|
+
|
|
26
|
+
- `validate-schema` only checks that `format` is a string. A format the platform does not support passes and then does nothing in App Studio. Read the `check-component` table for the formats warning, and compare with [config-schema.md](../../create-dx-component/references/config-schema.md).
|
|
27
|
+
- A `visibility` expression that references a variable the authoring runtime does not provide hides the property. Use only the forms listed in config-schema.md.
|
|
28
|
+
- A property whose `name` is not read by the component has no effect; `check-component` fails on that.
|
|
29
|
+
- A property saved before a change can leave an old value behind. See [update-dx-component](../../update-dx-component/SKILL.md).
|
|
30
|
+
|
|
31
|
+
## 3. It renders blank, or shows a generic failure message
|
|
32
|
+
|
|
33
|
+
The platform wraps every component in an error boundary, so a component that throws shows a generic failure message instead of crashing the page. The real error is in the browser console.
|
|
34
|
+
|
|
35
|
+
| Likely cause | Evidence and fix |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| An exception in render, often reading a property of `undefined` | The console stack trace names the file. Guard the absent value. Typical sources: a prop that arrives `null`, `children` treated as fields (they are regions), a missing optional config key |
|
|
38
|
+
| Missing `withConfiguration()` | The component does not register. Check the export |
|
|
39
|
+
| Missing `create-nonce` import | styled-components styles are blocked by the page's content security policy, so the component renders unstyled or broken. Keep the side-effect import in `index.tsx` |
|
|
40
|
+
| A prop arrives as a string when you expected a boolean or a number | `readOnly`, `required`, `disabled` and numeric settings can arrive as strings. Normalize them (see [field-behavior.md](../../create-dx-component/references/field-behavior.md)) |
|
|
41
|
+
| `PCore` or `getPConnect` used in a Presentation component | Not available there; the lint rule rejects it |
|
|
42
|
+
| A PCore or PConnect method that is not documented | It may not exist in the user's Infinity version. Check the API catalog |
|
|
43
|
+
|
|
44
|
+
## 4. It works in Storybook but not in a real view
|
|
45
|
+
|
|
46
|
+
Storybook uses the mocks you wrote. The usual causes are a mock that differs from the real payload shape, a platform value that is `null` or a string in real data, and a data page or property that the real application does not have. Ask the user for the real payload (with any customer data removed), compare it with `mock.ts`, and fix the component and the mock together.
|
|
47
|
+
|
|
48
|
+
To debug against the running platform with browser breakpoints, the CLI has `npm run startDevServer` ("debug runtime with browser break points using a local component server"; library mode). It is a long-running server, so the user runs it. Ask them to set a breakpoint where the console error occurs and report what they see.
|
|
49
|
+
|
|
50
|
+
## 5. It shows an old version
|
|
51
|
+
|
|
52
|
+
Confirm with the user that the new version was published and that they refreshed the application. Check `npm run showStatus` for the library version and whether it is permanent. Do not guess at server-side caching; say that you cannot see it and ask the user to check with their Pega administrator.
|
|
53
|
+
|
|
54
|
+
## 6. Still stuck
|
|
55
|
+
|
|
56
|
+
Summarize what was checked and what each check showed, list what you could not see, and tell the user exactly what to bring to Pega support or their administrator: component key, Infinity version, Cosmos version, the console error, and the `showStatus` output. Never include credentials, tokens or customer data.
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Runs every check a finished DX component needs and prints an evidence table.
|
|
3
|
+
// node .claude/skills/validate-component/scripts/check-component.mjs <Org>_<Library>_<Name> [--no-build]
|
|
4
|
+
// Each check ends as PASS, FAIL, WARN or NOT RUN. Paste the table into the final report; never claim a check that
|
|
5
|
+
// the table does not show as PASS. Exit code 1 means at least one FAIL.
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
9
|
+
import { pathToFileURL } from 'node:url';
|
|
10
|
+
|
|
11
|
+
// The 17 formats the CLI scaffolds use. Other formats pass validate-schema (it only checks that format is a string)
|
|
12
|
+
// but are not proven to work for custom components.
|
|
13
|
+
export const SCAFFOLD_FORMATS = new Set([
|
|
14
|
+
'GROUP',
|
|
15
|
+
'TEXT',
|
|
16
|
+
'SELECT',
|
|
17
|
+
'NUMBER',
|
|
18
|
+
'BOOLEAN',
|
|
19
|
+
'VISIBILITY',
|
|
20
|
+
'REQUIRED',
|
|
21
|
+
'DISABLED',
|
|
22
|
+
'READONLY',
|
|
23
|
+
'VALUEINPUT',
|
|
24
|
+
'LABEL',
|
|
25
|
+
'CONTENTPICKER',
|
|
26
|
+
'CASCADE',
|
|
27
|
+
'PROPERTY',
|
|
28
|
+
'INLINE',
|
|
29
|
+
'DATASOURCE',
|
|
30
|
+
'INSTRUCTIONS',
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
// Formats whose value the platform consumes itself, so the component code does not need to read the property.
|
|
34
|
+
const PLATFORM_HANDLED = new Set(['VISIBILITY', 'REQUIRED', 'DISABLED', 'READONLY', 'CONTENTPICKER', 'INSTRUCTIONS', 'LABEL', 'INLINE']);
|
|
35
|
+
|
|
36
|
+
const SECRET_PATTERNS = [
|
|
37
|
+
[/client[_-]?secret\s*[:=]\s*['"][^'"]+['"]/i, 'client secret'],
|
|
38
|
+
[/(password|passwd)\s*[:=]\s*['"][^'"]+['"]/i, 'password'],
|
|
39
|
+
[/bearer\s+[a-z0-9._-]{16,}/i, 'bearer token'],
|
|
40
|
+
[/(api[_-]?key|access[_-]?token)\s*[:=]\s*['"][^'"]{8,}['"]/i, 'API key or token'],
|
|
41
|
+
[/https?:\/\/[^\s'"]*\.(pegalabs|pega)\.[a-z.]+\/prweb/i, 'environment URL'],
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const stripComments = (text) => text.replace(/\/\*[\s\S]*?\*\//g, '').replace(/(^|[^:])\/\/.*$/gm, '$1');
|
|
45
|
+
|
|
46
|
+
/** Collect every property that carries a name, descending into groups and cascade sources. */
|
|
47
|
+
export const collectProperties = (properties = []) =>
|
|
48
|
+
properties.flatMap((property) => {
|
|
49
|
+
const own = property.name ? [property] : [];
|
|
50
|
+
return [
|
|
51
|
+
...own,
|
|
52
|
+
...collectProperties(property.properties),
|
|
53
|
+
...(property.source && !Array.isArray(property.source) ? collectProperties([property.source]) : []),
|
|
54
|
+
];
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/** Names declared in config.json that the component code never mentions. */
|
|
58
|
+
export const unconsumedProperties = (config, code) =>
|
|
59
|
+
collectProperties(config.properties)
|
|
60
|
+
.filter((property) => !PLATFORM_HANDLED.has(property.format))
|
|
61
|
+
.map((property) => property.name)
|
|
62
|
+
.filter((name) => !new RegExp(`\\b${name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(code));
|
|
63
|
+
|
|
64
|
+
/** Top-level authoring properties a story should expose as controls; DATASOURCE sub-fields are not props. */
|
|
65
|
+
export const controllableProperties = (properties = []) =>
|
|
66
|
+
properties.flatMap((property) => {
|
|
67
|
+
if (property.format === 'GROUP') return controllableProperties(property.properties);
|
|
68
|
+
return property.name && !PLATFORM_HANDLED.has(property.format) ? [property.name] : [];
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
/** Direct keys of the meta's argTypes object; nested settings, strings and comments are ignored. */
|
|
72
|
+
const argTypesKeys = (source) => {
|
|
73
|
+
const code = stripComments(source);
|
|
74
|
+
const start = code.search(/\bargTypes\s*:\s*\{/);
|
|
75
|
+
if (start < 0) return new Set();
|
|
76
|
+
const keys = new Set();
|
|
77
|
+
let depth = 0;
|
|
78
|
+
let expectKey = false;
|
|
79
|
+
let index = code.indexOf('{', start);
|
|
80
|
+
while (index < code.length) {
|
|
81
|
+
const char = code[index];
|
|
82
|
+
if (char === "'" || char === '"' || char === '`') {
|
|
83
|
+
let end = index + 1;
|
|
84
|
+
while (end < code.length && code[end] !== char) end += code[end] === '\\' ? 2 : 1;
|
|
85
|
+
if (depth === 1 && expectKey && char !== '`' && /^\s*:/.test(code.slice(end + 1))) keys.add(code.slice(index + 1, end));
|
|
86
|
+
expectKey = false;
|
|
87
|
+
index = end + 1;
|
|
88
|
+
} else if (/[A-Za-z_$]/.test(char)) {
|
|
89
|
+
const word = /^[\w$]+/.exec(code.slice(index))[0];
|
|
90
|
+
if (depth === 1 && expectKey && /^\s*:/.test(code.slice(index + word.length))) keys.add(word);
|
|
91
|
+
expectKey = false;
|
|
92
|
+
index += word.length;
|
|
93
|
+
} else {
|
|
94
|
+
if (char === '{' || char === '[' || char === '(') {
|
|
95
|
+
depth += 1;
|
|
96
|
+
expectKey = depth === 1 && char === '{';
|
|
97
|
+
} else if (char === '}' || char === ']' || char === ')') {
|
|
98
|
+
depth -= 1;
|
|
99
|
+
if (depth === 0) break;
|
|
100
|
+
} else if (char === ',') expectKey = depth === 1;
|
|
101
|
+
else if (!/\s/.test(char)) expectKey = false;
|
|
102
|
+
index += 1;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return keys;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/** Authoring properties with no entry in the story meta's argTypes. */
|
|
109
|
+
export const missingStoryControls = (config, storySource) => {
|
|
110
|
+
const keys = argTypesKeys(storySource);
|
|
111
|
+
return controllableProperties(config.properties).filter((name) => !keys.has(name));
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const unprovenFormats = (config) => {
|
|
115
|
+
const formats = new Set();
|
|
116
|
+
const walk = (properties = []) =>
|
|
117
|
+
properties.forEach((property) => {
|
|
118
|
+
if (property.format) formats.add(property.format);
|
|
119
|
+
walk(property.properties);
|
|
120
|
+
if (property.source && !Array.isArray(property.source)) walk([property.source]);
|
|
121
|
+
});
|
|
122
|
+
walk(config.properties);
|
|
123
|
+
return [...formats].filter((format) => !SCAFFOLD_FORMATS.has(format)).sort();
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export const findSecrets = (code) => SECRET_PATTERNS.filter(([pattern]) => pattern.test(stripComments(code))).map(([, label]) => label);
|
|
127
|
+
|
|
128
|
+
export const countTests = (testSource) => (testSource.match(/\b(it|test)(\.each)?\s*\(/g) || []).length;
|
|
129
|
+
export const countStories = (storySource) => (storySource.match(/export\s+const\s+\w+/g) || []).length;
|
|
130
|
+
|
|
131
|
+
// The commands are independent, so they run in parallel; spawnSync would serialize them.
|
|
132
|
+
const run = (command, args, { timeout = 600000, ...options } = {}) =>
|
|
133
|
+
new Promise((resolve) => {
|
|
134
|
+
let output = '';
|
|
135
|
+
let settled = false;
|
|
136
|
+
let timer;
|
|
137
|
+
const finish = (status, error) => {
|
|
138
|
+
if (settled) return;
|
|
139
|
+
settled = true;
|
|
140
|
+
clearTimeout(timer);
|
|
141
|
+
resolve({ status, output, error });
|
|
142
|
+
};
|
|
143
|
+
const isWindows = process.platform === 'win32';
|
|
144
|
+
const child = spawn(command, args, { shell: false, detached: !isWindows, ...options });
|
|
145
|
+
const killTree = () => {
|
|
146
|
+
try {
|
|
147
|
+
if (isWindows) spawnSync('taskkill', ['/pid', String(child.pid), '/T', '/F'], { stdio: 'ignore' });
|
|
148
|
+
else process.kill(-child.pid, 'SIGKILL');
|
|
149
|
+
} catch {
|
|
150
|
+
child.kill('SIGKILL');
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
timer = setTimeout(() => {
|
|
154
|
+
output += `\nTimed out after ${timeout / 1000}s`;
|
|
155
|
+
killTree();
|
|
156
|
+
child.stdout?.destroy();
|
|
157
|
+
child.stderr?.destroy();
|
|
158
|
+
finish(null);
|
|
159
|
+
}, timeout);
|
|
160
|
+
child.stdout?.on('data', (chunk) => (output += chunk));
|
|
161
|
+
child.stderr?.on('data', (chunk) => (output += chunk));
|
|
162
|
+
child.on('error', (error) => finish(null, error));
|
|
163
|
+
child.on('close', (status) => finish(status));
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Type-check only this component, the shared folder and the global declarations instead of the whole project
|
|
167
|
+
// (the project tsconfig also covers built output and every other component). Incremental, so re-runs are fast.
|
|
168
|
+
const scopedTsconfig = (key, cwd) => {
|
|
169
|
+
try {
|
|
170
|
+
const cacheDir = path.join(cwd, 'node_modules', '.cache', 'dxcb-check');
|
|
171
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
172
|
+
const fromCache = (target) => path.relative(cacheDir, path.join(cwd, target)).split(path.sep).join('/');
|
|
173
|
+
const file = path.join(cacheDir, `tsconfig.${key}.json`);
|
|
174
|
+
const tsconfig = {
|
|
175
|
+
extends: fromCache('tsconfig.json'),
|
|
176
|
+
include: ['src/*.d.ts', 'setupTests.ts', 'setupFiles.ts', `src/components/${key}/**/*`, 'src/components/shared/**/*'].map(fromCache),
|
|
177
|
+
compilerOptions: { noEmit: true, incremental: true, tsBuildInfoFile: `./${key}.tsbuildinfo` },
|
|
178
|
+
};
|
|
179
|
+
fs.writeFileSync(file, `${JSON.stringify(tsconfig, null, 2)}\n`);
|
|
180
|
+
return file;
|
|
181
|
+
} catch {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const tail = (text, lines = 6) => text.trim().split('\n').slice(-lines).join('\n');
|
|
187
|
+
|
|
188
|
+
const check = (name, status, detail = '') => ({ name, status, detail });
|
|
189
|
+
|
|
190
|
+
export const runChecks = async (key, { build = true, cwd = process.cwd(), env = process.env } = {}) => {
|
|
191
|
+
const results = [];
|
|
192
|
+
const folder = path.join(cwd, 'src', 'components', key);
|
|
193
|
+
if (!fs.existsSync(folder)) return [check('component folder', 'FAIL', `src/components/${key} does not exist`)];
|
|
194
|
+
results.push(check('component folder', 'PASS', `src/components/${key}`));
|
|
195
|
+
|
|
196
|
+
const tsconfig = fs.existsSync(path.join(cwd, 'tsconfig.json')) ? scopedTsconfig(key, cwd) : undefined;
|
|
197
|
+
const pending = {
|
|
198
|
+
schema: run('npm', ['run', 'validate-schema', '--', key], { cwd, env }),
|
|
199
|
+
tsc: run('npx', tsconfig ? ['tsc', '-p', tsconfig] : ['tsc', '--noEmit'], { cwd, env }),
|
|
200
|
+
lint: run('npx', ['eslint', `src/components/${key}`], { cwd, env }),
|
|
201
|
+
jest: run('npx', ['jest', `src/components/${key}`, '--coverage=false'], { cwd, env }),
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const read = (file) => (fs.existsSync(path.join(folder, file)) ? fs.readFileSync(path.join(folder, file), 'utf8') : '');
|
|
205
|
+
const sourceFiles = fs
|
|
206
|
+
.readdirSync(folder)
|
|
207
|
+
.filter(
|
|
208
|
+
(file) =>
|
|
209
|
+
/\.(ts|tsx)$/.test(file) && !/^demo\./.test(file) && file !== 'mock.ts' && !file.endsWith('.d.ts') && !/\.(test|stories)\./.test(file),
|
|
210
|
+
);
|
|
211
|
+
const code = sourceFiles.map(read).join('\n');
|
|
212
|
+
|
|
213
|
+
const schema = await pending.schema;
|
|
214
|
+
const schemaPassed = schema.status === 0 && /valid/i.test(schema.output);
|
|
215
|
+
results.push(check('config.json schema', schemaPassed ? 'PASS' : 'FAIL', tail(schema.output, 3)));
|
|
216
|
+
|
|
217
|
+
let config = {};
|
|
218
|
+
try {
|
|
219
|
+
config = JSON.parse(read('config.json'));
|
|
220
|
+
} catch (error) {
|
|
221
|
+
results.push(check('config.json parses', 'FAIL', String(error.message)));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const formats = unprovenFormats(config);
|
|
225
|
+
results.push(
|
|
226
|
+
check(
|
|
227
|
+
'formats are scaffold-proven',
|
|
228
|
+
formats.length ? 'WARN' : 'PASS',
|
|
229
|
+
formats.length ? `not proven for custom components: ${formats.join(', ')}` : 'all formats are used by the CLI scaffolds',
|
|
230
|
+
),
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
const missing = unconsumedProperties(config, code);
|
|
234
|
+
results.push(
|
|
235
|
+
check(
|
|
236
|
+
'config properties are consumed',
|
|
237
|
+
missing.length ? 'FAIL' : 'PASS',
|
|
238
|
+
missing.length ? `never referenced in the component: ${missing.join(', ')}` : 'every authoring property is read by the code',
|
|
239
|
+
),
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
const lint = await pending.lint;
|
|
243
|
+
const lintPassed = lint.status === 0;
|
|
244
|
+
|
|
245
|
+
// `npm run validate` checks the schema and lints before it bundles and stops at the first failure, so it cannot
|
|
246
|
+
// pass while either of those fails. Start it as soon as both pass, alongside tsc and Jest.
|
|
247
|
+
let built;
|
|
248
|
+
if (build && schemaPassed && lintPassed) built = run('npm', ['run', 'validate', '--', key, 'N', 'N'], { cwd, env, timeout: 900000 });
|
|
249
|
+
|
|
250
|
+
const tsc = await pending.tsc;
|
|
251
|
+
const tscErrors = tsc.output
|
|
252
|
+
.split('\n')
|
|
253
|
+
.filter((line) => line.includes(`src/components/${key}/`) && line.includes('error TS') && !line.includes('demo.stories'));
|
|
254
|
+
// Errors with no file (a broken tsconfig, nothing to compile) would otherwise pass unnoticed.
|
|
255
|
+
const tscGlobalErrors = tsc.output.split('\n').filter((line) => /^error TS\d+/.test(line));
|
|
256
|
+
const tscProblems = [...tscErrors, ...tscGlobalErrors, ...(tsc.error ? [String(tsc.error.message)] : [])];
|
|
257
|
+
results.push(check('tsc (component files)', tscProblems.length ? 'FAIL' : 'PASS', tscProblems.slice(0, 5).join('\n')));
|
|
258
|
+
|
|
259
|
+
results.push(check('eslint', lintPassed ? 'PASS' : 'FAIL', lintPassed ? '' : tail(lint.output, 8)));
|
|
260
|
+
|
|
261
|
+
const jest = await pending.jest;
|
|
262
|
+
const summary = jest.output.match(/Tests:\s+(.*)/);
|
|
263
|
+
results.push(check('jest', jest.status === 0 ? 'PASS' : 'FAIL', summary ? summary[1] : tail(jest.output, 5)));
|
|
264
|
+
|
|
265
|
+
const tests = countTests(read('demo.test.tsx'));
|
|
266
|
+
results.push(
|
|
267
|
+
check('tests beyond the scaffold', tests > 1 ? 'PASS' : 'WARN', `${tests} test(s) in demo.test.tsx; cover each state in state-matrix.md`),
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
const stories = countStories(read('demo.stories.tsx'));
|
|
271
|
+
results.push(
|
|
272
|
+
check('stories per state', stories > 1 ? 'PASS' : 'WARN', `${stories} story export(s) in demo.stories.tsx; one story per applicable state`),
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
const noControls = missingStoryControls(config, read('demo.stories.tsx'));
|
|
276
|
+
results.push(
|
|
277
|
+
check(
|
|
278
|
+
'story controls',
|
|
279
|
+
noControls.length ? 'WARN' : 'PASS',
|
|
280
|
+
noControls.length ? `no argTypes entry in demo.stories.tsx for: ${noControls.join(', ')}` : 'every authoring property has a story control',
|
|
281
|
+
),
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
const inlineStyle = /style=\{\{/.test(code);
|
|
285
|
+
const dangerous = /dangerouslySetInnerHTML/.test(code);
|
|
286
|
+
results.push(check('no inline styles', inlineStyle ? 'FAIL' : 'PASS', inlineStyle ? 'style={{ ... }} found; use Cosmos props or styles.ts' : ''));
|
|
287
|
+
results.push(check('no dangerouslySetInnerHTML', dangerous ? 'FAIL' : 'PASS', dangerous ? 'raw HTML injection found' : ''));
|
|
288
|
+
|
|
289
|
+
const secrets = findSecrets(`${code}\n${read('config.json')}\n${read('mock.ts')}`);
|
|
290
|
+
results.push(check('no secrets or environment URLs', secrets.length ? 'FAIL' : 'PASS', secrets.length ? `found: ${secrets.join(', ')}` : ''));
|
|
291
|
+
|
|
292
|
+
if (built) {
|
|
293
|
+
const result = await built;
|
|
294
|
+
const failed = result.status !== 0 || /FAILED|Error while|TypeError/.test(result.output);
|
|
295
|
+
results.push(check('build (lint, schema, bundle)', failed ? 'FAIL' : 'PASS', failed ? tail(result.output, 6) : ''));
|
|
296
|
+
} else if (build) {
|
|
297
|
+
results.push(check('build (lint, schema, bundle)', 'NOT RUN', 'skipped because the schema or ESLint check failed; fix those first'));
|
|
298
|
+
} else {
|
|
299
|
+
results.push(check('build (lint, schema, bundle)', 'NOT RUN', 'skipped with --no-build'));
|
|
300
|
+
}
|
|
301
|
+
return results;
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
export const formatReport = (key, results) => {
|
|
305
|
+
const count = (status) => results.filter((result) => result.status === status).length;
|
|
306
|
+
const lines = [`Check report for ${key}`, '', '| Check | Result | Detail |', '| --- | --- | --- |'];
|
|
307
|
+
results.forEach(({ name, status, detail }) =>
|
|
308
|
+
lines.push(`| ${name} | ${status} | ${detail.split('\n').join(' ').slice(0, 200).replace(/\|/g, '\\|')} |`),
|
|
309
|
+
);
|
|
310
|
+
lines.push('', `RESULT: ${count('PASS')} passed, ${count('FAIL')} failed, ${count('WARN')} warnings, ${count('NOT RUN')} not run`);
|
|
311
|
+
if (count('FAIL')) lines.push('Do not report this component as done until every FAIL is fixed or explained to the user.');
|
|
312
|
+
return lines.join('\n');
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const main = async () => {
|
|
316
|
+
const args = process.argv.slice(2);
|
|
317
|
+
const key = args.find((arg) => !arg.startsWith('--'));
|
|
318
|
+
if (!key || !/^[A-Za-z0-9]+_[A-Za-z0-9]+_[A-Za-z0-9]+$/.test(key)) {
|
|
319
|
+
console.error('Usage: node check-component.mjs <Org>_<Library>_<Name> [--no-build]');
|
|
320
|
+
process.exit(2);
|
|
321
|
+
}
|
|
322
|
+
const results = await runChecks(key, { build: !args.includes('--no-build') });
|
|
323
|
+
console.log(formatReport(key, results));
|
|
324
|
+
process.exit(results.some((result) => result.status === 'FAIL') ? 1 : 0);
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) main();
|