@astryxdesign/cli 0.7.2-canary.fe2c655 → 0.7.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/CHANGELOG.md +119 -0
- package/README.md +175 -154
- package/api/build/_adapter.d.mts +8 -0
- package/api/build/_adapter.mjs +12 -1
- package/api/build/build.doc.mjs +4 -0
- package/api/build/help/help.mjs +1 -1
- package/api/build/kit/kit.mjs +3 -11
- package/api/component/_adapter.d.mts +72 -1
- package/api/component/_adapter.mjs +212 -18
- package/api/component/component.doc.mjs +6 -1
- package/api/component/component.mjs +82 -15
- package/api/component/component.test.mjs +80 -0
- package/api/component/doc-links.test.mjs +386 -0
- package/api/component/integration-export-name.test.mjs +644 -0
- package/api/component/list/list.mjs +42 -4
- package/api/discover/_adapter.d.mts +132 -12
- package/api/discover/_adapter.mjs +488 -27
- package/api/discover/_adapter.test.mjs +599 -17
- package/api/discover/_catalog-view.d.mts +16 -5
- package/api/discover/_catalog-view.mjs +32 -8
- package/api/discover/detail/doc/doc.mjs +9 -1
- package/api/discover/discover.d.mts +11 -0
- package/api/discover/discover.doc.mjs +9 -7
- package/api/discover/discover.mjs +121 -19
- package/api/discover/discover.type.d.mts +20 -7
- package/api/discover/discover.type.mjs +17 -7
- package/api/discover/list/list.d.mts +4 -3
- package/api/discover/list/list.mjs +17 -10
- package/api/docs/_adapter.d.mts +74 -0
- package/api/docs/_adapter.mjs +553 -16
- package/api/docs/docs.doc.mjs +2 -1
- package/api/docs/docs.mjs +2 -1
- package/api/docs/docs.test.mjs +5 -2
- package/api/docs/integration-tree.test.mjs +22 -0
- package/api/docs/integrationDocs.test.mjs +124 -0
- package/api/docs/list/list.mjs +12 -2
- package/api/docs/reference-blocks.test.mjs +1 -3
- package/api/doctor/_adapter.d.mts +108 -0
- package/api/doctor/_adapter.mjs +223 -4
- package/api/doctor/doctor.d.mts +199 -0
- package/api/doctor/doctor.doc.mjs +3 -2
- package/api/doctor/doctor.mjs +530 -34
- package/api/doctor/doctor.test.mjs +180 -14
- package/api/doctor/release-checks.test.mjs +156 -0
- package/api/doctor/theme-checks.mjs +17 -4
- package/api/doctor/theme-checks.test.mjs +223 -7
- package/api/error.d.mts +10 -0
- package/api/error.mjs +25 -0
- package/api/gap-report/gap-report.d.mts +3 -0
- package/api/gap-report/gap-report.doc.mjs +11 -3
- package/api/gap-report/gap-report.mjs +219 -52
- package/api/gap-report/gap-report.test.mjs +233 -0
- package/api/gap-report/gap-report.type.d.mts +41 -0
- package/api/gap-report/gap-report.type.mjs +18 -0
- package/api/hook/_adapter.mjs +3 -11
- package/api/hook/hook.doc.mjs +4 -0
- package/api/hook/list/list.d.mts +1 -1
- package/api/index.d.mts +2 -0
- package/api/index.mjs +2 -0
- package/api/init/_adapter.d.mts +22 -0
- package/api/init/_adapter.mjs +59 -0
- package/api/init/init.d.mts +7 -5
- package/api/init/init.doc.mjs +28 -7
- package/api/init/init.mjs +28 -4
- package/api/init/init.type.d.mts +48 -3
- package/api/init/init.type.mjs +37 -3
- package/api/init/run/run.d.mts +1 -1
- package/api/init/run/run.mjs +17 -6
- package/api/init/user/user.d.mts +14 -0
- package/api/init/user/user.mjs +87 -0
- package/api/init/user/user.test.mjs +232 -0
- package/api/integration/_adapter.d.mts +45 -0
- package/api/integration/_adapter.mjs +112 -0
- package/api/integration/add-contribution.component-names.test.mjs +66 -0
- package/api/integration/add-contribution.mjs +8 -4
- package/api/integration/add-contribution.test.mjs +4 -2
- package/api/integration/authoring-checks.mjs +110 -6
- package/api/integration/authoring-checks.test.mjs +808 -0
- package/api/integration/authoring-checks.type.d.mts +143 -0
- package/api/integration/authoring-checks.type.mjs +74 -2
- package/api/integration/check/check.d.mts +96 -0
- package/api/integration/check/check.mjs +357 -0
- package/api/integration/check/check.test.mjs +489 -0
- package/api/integration/integration-check.type.d.mts +118 -0
- package/api/integration/integration-check.type.mjs +84 -0
- package/api/integration/integrationCheck.doc.d.mts +10 -0
- package/api/integration/integrationCheck.doc.mjs +73 -0
- package/api/integration/integrationComponentConflicts.doc.mjs +45 -6
- package/api/integration/integrationDocConflicts.doc.mjs +22 -5
- package/api/integration/pack-check.mjs +364 -120
- package/api/integration/pack-check.test.mjs +730 -10
- package/api/integration/validate-integration-fixes.test.mjs +67 -0
- package/api/integration/validate-integration.d.mts +8 -0
- package/api/integration/validate-integration.mjs +63 -9
- package/api/integration/validate-integration.test.mjs +88 -8
- package/api/integration/validate-integration.type.d.mts +16 -0
- package/api/integration/validate-integration.type.mjs +14 -1
- package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
- package/api/integration/validateIntegration.doc.mjs +19 -3
- package/api/search/_adapter.d.mts +11 -0
- package/api/search/_adapter.mjs +25 -0
- package/api/search/search.d.mts +82 -13
- package/api/search/search.doc.mjs +8 -3
- package/api/search/search.domain-priority.test.mjs +108 -18
- package/api/search/search.integration-check.test.mjs +24 -0
- package/api/search/search.mjs +645 -64
- package/api/search/search.test.mjs +179 -1
- package/api/search/search.type.d.mts +14 -1
- package/api/search/search.type.mjs +6 -1
- package/api/swizzle/_adapter.mjs +2 -2
- package/api/swizzle/copy/copy.mjs +29 -15
- package/api/template/template-integration.test.mjs +1 -1
- package/api/template/template.test.mjs +120 -3
- package/api/theme/_adapter.d.mts +16 -0
- package/api/theme/_adapter.mjs +32 -5
- package/api/theme/build/build.d.mts +2 -1
- package/api/theme/build/build.family.test.mjs +64 -0
- package/api/theme/build/build.mjs +250 -38
- package/api/theme/build/build.packed-old-core.test.mjs +20 -3
- package/api/theme/build/font-warning.mjs +5 -3
- package/api/theme/build/font-warning.test.mjs +2 -1
- package/api/theme/remove/remove.test.mjs +2 -2
- package/api/theme/targets/targets.mjs +3 -10
- package/api/theme/themeBuild.doc.mjs +4 -4
- package/api/theme/themeTargets.doc.mjs +4 -0
- package/api/theme/use/use.test.mjs +1 -1
- package/api/upgrade/_adapter.d.mts +40 -2
- package/api/upgrade/_adapter.mjs +118 -5
- package/api/upgrade/run/cli-behind-core.test.mjs +438 -0
- package/api/upgrade/run/run.mjs +35 -2
- package/api/upgrade/status/status.d.mts +21 -5
- package/api/upgrade/status/status.mjs +45 -8
- package/api/upgrade/upgrade.doc.mjs +6 -3
- package/api/upgrade/upgrade.mjs +5 -1
- package/api/upgrade/upgrade.type.d.mts +23 -0
- package/api/upgrade/upgrade.type.mjs +16 -1
- package/assets/codemods/__tests__/registry.test.mjs +1 -0
- package/assets/codemods/registry.mjs +1 -0
- package/assets/codemods/runner.mjs +7 -1
- package/assets/codemods/transforms/v0.7.2/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +120 -0
- package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
- package/assets/codemods/transforms/v0.7.2/migrate-list-edge-compensation-to-edge.mjs +198 -0
- package/assets/discover/official-packages.json +367 -0
- package/assets/docs/README.md +10 -0
- package/assets/docs/author-a-theme.doc.mjs +3 -3
- package/assets/docs/browser-support.doc.mjs +1 -1
- package/assets/docs/getting-started.doc.mjs +26 -6
- package/assets/docs/styling-overview.doc.mjs +1 -1
- package/assets/docs/theme.doc.dense.mjs +1 -1
- package/assets/docs/theme.doc.mjs +2 -2
- package/assets/docs/theme.doc.zh.mjs +1 -1
- package/assets/docs/tree/add-a-component.doc.mjs +22 -2
- package/assets/docs/tree/add-a-theme.doc.mjs +13 -3
- package/assets/docs/tree/add-a-topic.doc.mjs +54 -9
- package/assets/docs/tree/agent-guidance.doc.mjs +19 -5
- package/assets/docs/tree/block-template.doc.mjs +8 -20
- package/assets/docs/tree/build-the-template.doc.mjs +1 -1
- package/assets/docs/tree/building-blocks.doc.mjs +4 -0
- package/assets/docs/tree/check-the-props.doc.mjs +201 -0
- package/assets/docs/tree/check-your-docs.doc.mjs +69 -11
- package/assets/docs/tree/checks.doc.mjs +32 -24
- package/assets/docs/tree/ci-and-containers.doc.mjs +88 -0
- package/assets/docs/tree/cli-command.doc.mjs +142 -0
- package/assets/docs/tree/cli.doc.mjs +6 -0
- package/assets/docs/tree/codemods.doc.mjs +17 -7
- package/assets/docs/tree/commands.doc.mjs +7 -1
- package/assets/docs/tree/component-family.doc.mjs +8 -6
- package/assets/docs/tree/component-imports.doc.mjs +21 -3
- package/assets/docs/tree/components.doc.mjs +7 -1
- package/assets/docs/tree/computer-agent-guide.doc.mjs +160 -0
- package/assets/docs/tree/computer-integrations.doc.mjs +93 -0
- package/assets/docs/tree/configuration.doc.mjs +7 -3
- package/assets/docs/tree/debug-and-gap-reports.doc.mjs +41 -22
- package/assets/docs/tree/define-the-theme.doc.mjs +6 -5
- package/assets/docs/tree/describe-the-component.doc.mjs +3 -3
- package/assets/docs/tree/docs.doc.mjs +9 -3
- package/assets/docs/tree/document-the-template.doc.mjs +1 -1
- package/assets/docs/tree/document-the-theme.doc.mjs +3 -3
- package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
- package/assets/docs/tree/extend-or-replace.doc.mjs +19 -9
- package/assets/docs/tree/fonts-and-assets.doc.mjs +2 -2
- package/assets/docs/tree/generate-a-palette.doc.mjs +7 -5
- package/assets/docs/tree/global-integrations.doc.mjs +124 -0
- package/assets/docs/tree/global.doc.mjs +131 -0
- package/assets/docs/tree/grade-template-with-agent.doc.mjs +5 -5
- package/assets/docs/tree/install-once.doc.mjs +105 -0
- package/assets/docs/tree/integrations.doc.mjs +13 -1
- package/assets/docs/tree/layout-spacing.doc.mjs +2 -3
- package/assets/docs/tree/layout.doc.mjs +6 -6
- package/assets/docs/tree/links.doc.mjs +7 -6
- package/assets/docs/tree/migration-setup.doc.mjs +3 -2
- package/assets/docs/tree/migration-verification.doc.mjs +0 -1
- package/assets/docs/tree/migration.doc.mjs +0 -1
- package/assets/docs/tree/monorepos.doc.mjs +84 -0
- package/assets/docs/tree/package-and-test.doc.mjs +1 -1
- package/assets/docs/tree/page-template.doc.mjs +4 -8
- package/assets/docs/tree/problems.doc.mjs +205 -0
- package/assets/docs/tree/project-global-or-both.doc.mjs +131 -0
- package/assets/docs/tree/publishing.doc.mjs +6 -6
- package/assets/docs/tree/quick-start.doc.mjs +53 -27
- package/assets/docs/tree/reference.doc.mjs +192 -0
- package/assets/docs/tree/replace-a-core-component.doc.mjs +15 -7
- package/assets/docs/tree/replace-a-core-template.doc.mjs +10 -12
- package/assets/docs/tree/scaffold.doc.mjs +1 -1
- package/assets/docs/tree/sections-and-placement.doc.mjs +9 -11
- package/assets/docs/tree/see-it-in-an-app.doc.mjs +17 -5
- package/assets/docs/tree/short-and-findable.doc.mjs +9 -8
- package/assets/docs/tree/single-component.doc.mjs +15 -6
- package/assets/docs/tree/start-a-template.doc.mjs +18 -8
- package/assets/docs/tree/subcomponent.doc.mjs +6 -6
- package/assets/docs/tree/template-assets.doc.mjs +2 -2
- package/assets/docs/tree/template-doc-overview.doc.mjs +6 -17
- package/assets/docs/tree/template-fonts.doc.mjs +1 -1
- package/assets/docs/tree/template-icons.doc.mjs +1 -1
- package/assets/docs/tree/template-images-media.doc.mjs +1 -1
- package/assets/docs/tree/template-styles.doc.mjs +2 -2
- package/assets/docs/tree/templates.doc.mjs +7 -1
- package/assets/docs/tree/test-in-an-app.doc.mjs +7 -7
- package/assets/docs/tree/test-template-in-app.doc.mjs +5 -5
- package/assets/docs/tree/themes.doc.mjs +4 -0
- package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
- package/assets/docs/tree/troubleshooting.doc.mjs +29 -13
- package/assets/docs/tree/type-scale.doc.mjs +3 -3
- package/assets/docs/tree/uninstall.doc.mjs +109 -0
- package/assets/docs/tree/updates.doc.mjs +92 -0
- package/assets/docs/tree/upgrading.doc.mjs +6 -6
- package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +14 -2
- package/assets/docs/tree/verify-packed-template.doc.mjs +3 -3
- package/assets/docs/tree/versioning.doc.mjs +10 -9
- package/assets/docs/tree/who-answers.doc.mjs +119 -0
- package/assets/docs/tree/write-the-template-file.doc.mjs +7 -6
- package/assets/docs/use-a-theme.doc.mjs +2 -2
- package/assets/docs/working-with-ai.doc.mjs +66 -13
- package/assets/theme.template.ts +1 -1
- package/authoring/codemod/codemod.doc.mjs +7 -3
- package/authoring/config/config.doc.mjs +9 -6
- package/authoring/config/type.ts +4 -2
- package/authoring/debug/parse.d.mts +5 -5
- package/authoring/discover/discover.doc.mjs +7 -1
- package/authoring/doctypes/_schema.d.mts +8 -3
- package/authoring/doctypes/_schema.mjs +1 -0
- package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
- package/authoring/doctypes/command/command.doc.mjs +20 -12
- package/authoring/doctypes/command/type.ts +14 -0
- package/authoring/doctypes/component/component.doc.mjs +30 -27
- package/authoring/doctypes/component/type.ts +21 -8
- package/authoring/doctypes/enum/enum.doc.mjs +7 -4
- package/authoring/doctypes/function/function.doc.mjs +9 -8
- package/authoring/doctypes/hook/hook.doc.mjs +5 -4
- package/authoring/doctypes/hook/type.ts +4 -0
- package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
- package/authoring/doctypes/reference/reference.doc.mjs +12 -11
- package/authoring/doctypes/schema/schema.doc.mjs +9 -9
- package/authoring/doctypes/template/integration-template.doc.d.mts +11 -0
- package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
- package/authoring/doctypes/template/parse.d.mts +6 -2
- package/authoring/doctypes/template/parse.mjs +3 -1
- package/authoring/doctypes/template/template.doc.mjs +3 -3
- package/authoring/doctypes/template/type.ts +22 -0
- package/authoring/doctypes/theme/theme.doc.mjs +2 -2
- package/authoring/gap-report/gap-report.doc.mjs +2 -2
- package/authoring/identity/identity.doc.mjs +3 -3
- package/authoring/index.d.ts +2 -0
- package/authoring/integration/integration.doc.mjs +30 -6
- package/authoring/integration/parse.test.mjs +21 -0
- package/authoring/integration/schema.d.mts +16 -3
- package/authoring/integration/schema.mjs +34 -2
- package/authoring/integration/type.ts +12 -0
- package/clients/cli/bin/astryx.mjs +20 -3
- package/clients/cli/cli-command.test.mjs +166 -0
- package/clients/cli/commands/blog.doc.mjs +1 -0
- package/clients/cli/commands/build-theme.batch-report.test.mjs +17 -3
- package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
- package/clients/cli/commands/build-theme.mjs +26 -13
- package/clients/cli/commands/build-theme.path-safety.test.mjs +224 -6
- package/clients/cli/commands/build-theme.registry.test.mjs +5 -2
- package/clients/cli/commands/build.doc.mjs +1 -0
- package/clients/cli/commands/build.exit-codes-doc.test.mjs +4 -0
- package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
- package/clients/cli/commands/component.doc.mjs +1 -0
- package/clients/cli/commands/discover.broken-integration.test.mjs +11 -7
- package/clients/cli/commands/discover.doc.mjs +9 -1
- package/clients/cli/commands/discover.mjs +144 -93
- package/clients/cli/commands/discover.no-source.test.mjs +448 -33
- package/clients/cli/commands/discover.sources.test.mjs +20 -1
- package/clients/cli/commands/docs.doc.mjs +1 -0
- package/clients/cli/commands/docs.mjs +49 -19
- package/clients/cli/commands/docs.test.mjs +2 -2
- package/clients/cli/commands/doctor-integration-components.doc.mjs +19 -4
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +20 -5
- package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -0
- package/clients/cli/commands/doctor-integration-validate.doc.mjs +22 -2
- package/clients/cli/commands/doctor-integration.test.mjs +888 -3
- package/clients/cli/commands/doctor.doc.mjs +12 -2
- package/clients/cli/commands/doctor.mjs +292 -7
- package/clients/cli/commands/doctor.test.mjs +10 -4
- package/clients/cli/commands/gap-report.doc.mjs +15 -2
- package/clients/cli/commands/gap-report.mjs +24 -2
- package/clients/cli/commands/gap-report.test.mjs +79 -0
- package/clients/cli/commands/hook.doc.mjs +1 -0
- package/clients/cli/commands/init.behavior.test.mjs +5 -0
- package/clients/cli/commands/init.doc.mjs +29 -3
- package/clients/cli/commands/integration-add.doc.mjs +7 -0
- package/clients/cli/commands/integration-authoring.test.mjs +49 -3
- package/clients/cli/commands/integration-check.doc.mjs +93 -0
- package/clients/cli/commands/integration-check.test.mjs +441 -0
- package/clients/cli/commands/integration-pack.doc.mjs +8 -1
- package/clients/cli/commands/integration-verify.doc.mjs +8 -1
- package/clients/cli/commands/integration.doc.mjs +6 -2
- package/clients/cli/commands/integration.mjs +119 -7
- package/clients/cli/commands/json-contract.test.mjs +11 -2
- package/clients/cli/commands/manifest.doc.mjs +1 -0
- package/clients/cli/commands/outside-project.test.mjs +774 -0
- package/clients/cli/commands/search.doc.mjs +1 -0
- package/clients/cli/commands/search.mjs +16 -11
- package/clients/cli/commands/search.test.mjs +429 -296
- package/clients/cli/commands/setup-nudge.test.mjs +102 -2
- package/clients/cli/commands/swizzle.doc.mjs +7 -0
- package/clients/cli/commands/swizzle.mjs +5 -3
- package/clients/cli/commands/swizzle.routing.test.mjs +19 -2
- package/clients/cli/commands/template.doc.mjs +1 -0
- package/clients/cli/commands/text-json-parity.test.mjs +5 -0
- package/clients/cli/commands/theme-add-lifecycle.test.mjs +3 -3
- package/clients/cli/commands/theme-add.doc.mjs +8 -1
- package/clients/cli/commands/theme-build.doc.mjs +3 -2
- package/clients/cli/commands/theme-eject.doc.mjs +1 -0
- package/clients/cli/commands/theme-list.behavior.test.mjs +6 -0
- package/clients/cli/commands/theme-list.doc.mjs +1 -0
- package/clients/cli/commands/theme-palette-generate.doc.mjs +1 -0
- package/clients/cli/commands/theme-remove.doc.mjs +7 -0
- package/clients/cli/commands/theme-targets.doc.mjs +1 -0
- package/clients/cli/commands/theme-template.doc.mjs +1 -0
- package/clients/cli/commands/theme-use.doc.mjs +7 -0
- package/clients/cli/commands/upgrade.config-ordering.test.mjs +2 -1
- package/clients/cli/commands/upgrade.doc.mjs +22 -1
- package/clients/cli/commands/upgrade.file-protection.test.mjs +3 -2
- package/clients/cli/commands/upgrade.hook-output.test.mjs +2 -1
- package/clients/cli/commands/upgrade.older-cli.test.mjs +160 -0
- package/clients/cli/index.mjs +112 -16
- package/clients/cli/lib/cli-error.mjs +4 -1
- package/clients/cli/lib/component-format.test.mjs +1 -1
- package/clients/cli/lib/define-command.mjs +2 -2
- package/clients/cli/lib/json-shim.mjs +3 -1
- package/clients/cli/lib/manifest.d.ts +9 -0
- package/clients/cli/lib/manifest.mjs +17 -1
- package/clients/cli/lib/manifest.test.mjs +25 -0
- package/foundation/agent-docs/agent-doc-state.d.mts +35 -6
- package/foundation/agent-docs/agent-doc-state.mjs +66 -6
- package/foundation/agent-docs/agent-docs.d.mts +4 -3
- package/foundation/agent-docs/agent-docs.mjs +11 -9
- package/foundation/agent-docs/computer-agent-text.d.mts +133 -0
- package/foundation/agent-docs/computer-agent-text.mjs +623 -0
- package/foundation/agent-docs/computer-agent-text.test.mjs +602 -0
- package/foundation/config/project.d.mts +35 -3
- package/foundation/config/project.mjs +226 -7
- package/foundation/config/project.test.mjs +66 -0
- package/foundation/config/theme-state.mjs +5 -4
- package/foundation/discovery/authoring-self-docs.mjs +2 -1
- package/foundation/discovery/cli-self-docs.mjs +20 -3
- package/foundation/discovery/cli-self-docs.test.mjs +3 -2
- package/foundation/discovery/component-discovery.d.mts +190 -10
- package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
- package/foundation/discovery/component-discovery.mjs +864 -56
- package/foundation/discovery/component-discovery.test.mjs +96 -0
- package/foundation/discovery/component-replacement.mjs +20 -1
- package/foundation/discovery/component-replacement.test.mjs +40 -0
- package/foundation/discovery/copied-passages.d.mts +216 -0
- package/foundation/discovery/copied-passages.mjs +720 -0
- package/foundation/discovery/copied-passages.test.mjs +124 -0
- package/foundation/discovery/doc-consistency.d.mts +34 -0
- package/foundation/discovery/doc-consistency.mjs +513 -0
- package/foundation/discovery/doc-consistency.test.mjs +347 -0
- package/foundation/discovery/doc-fluff.d.mts +112 -0
- package/foundation/discovery/doc-fluff.mjs +328 -0
- package/foundation/discovery/doc-fluff.test.mjs +142 -0
- package/foundation/discovery/doc-types.d.mts +157 -0
- package/foundation/discovery/doc-types.mjs +1231 -0
- package/foundation/discovery/doc-types.test.mjs +744 -0
- package/foundation/discovery/docs-discovery.d.mts +88 -0
- package/foundation/discovery/docs-discovery.mjs +83 -2
- package/foundation/discovery/integration-doc-files.d.mts +22 -0
- package/foundation/discovery/integration-doc-files.mjs +231 -0
- package/foundation/discovery/integration-doc-files.test.mjs +246 -0
- package/foundation/discovery/module-exports.d.mts +27 -0
- package/foundation/discovery/module-exports.mjs +356 -31
- package/foundation/discovery/module-exports.test.mjs +172 -0
- package/foundation/discovery/package-tsconfig.d.mts +14 -0
- package/foundation/discovery/package-tsconfig.mjs +228 -0
- package/foundation/discovery/package-tsconfig.test.mjs +210 -0
- package/foundation/discovery/project-typescript.d.mts +32 -0
- package/foundation/discovery/project-typescript.mjs +89 -0
- package/foundation/discovery/prop-drift.d.mts +297 -0
- package/foundation/discovery/prop-drift.mjs +2428 -0
- package/foundation/discovery/prop-drift.test.mjs +2337 -0
- package/foundation/discovery/runner-allowlist.d.mts +40 -0
- package/foundation/discovery/runner-allowlist.mjs +53 -0
- package/foundation/discovery/stray-pointers.d.mts +52 -0
- package/foundation/discovery/stray-pointers.mjs +293 -0
- package/foundation/discovery/stray-pointers.test.mjs +86 -0
- package/foundation/discovery/template-adapter.mjs +74 -5
- package/foundation/discovery/template-needs.mjs +5 -2
- package/foundation/discovery/theme-imports.mjs +8 -4
- package/foundation/discovery/theme-imports.test.mjs +1 -1
- package/foundation/discovery/type-match.d.mts +53 -0
- package/foundation/discovery/type-match.mjs +660 -0
- package/foundation/discovery/type-match.test.mjs +169 -0
- package/foundation/discovery/type-text.d.mts +68 -0
- package/foundation/discovery/type-text.mjs +658 -0
- package/foundation/discovery/type-text.test.mjs +196 -0
- package/foundation/discovery/unresolved-references.d.mts +62 -0
- package/foundation/discovery/unresolved-references.mjs +790 -0
- package/foundation/discovery/unresolved-references.test.mjs +567 -0
- package/foundation/doc-compiler/doc-loads.test.mjs +51 -1
- package/foundation/doc-compiler/inputs.mjs +27 -1
- package/foundation/doc-compiler/inputs.test.mjs +0 -1
- package/foundation/doc-compiler/links.d.mts +41 -5
- package/foundation/doc-compiler/links.mjs +167 -28
- package/foundation/doc-compiler/links.test.mjs +97 -4
- package/foundation/doc-compiler/read.d.mts +1 -1
- package/foundation/doc-compiler/tree.mjs +30 -1
- package/foundation/doc-compiler/tree.test.mjs +8 -1
- package/foundation/env/cli-command.d.mts +83 -0
- package/foundation/env/cli-command.mjs +152 -0
- package/foundation/env/cli-command.test.mjs +129 -0
- package/foundation/env/node-version.test.mjs +15 -3
- package/foundation/env/package-manager.d.mts +93 -9
- package/foundation/env/package-manager.global.test.mjs +66 -0
- package/foundation/env/package-manager.mjs +184 -13
- package/foundation/env/package-manager.test.mjs +341 -15
- package/foundation/env/project-scope.d.mts +9 -0
- package/foundation/env/project-scope.mjs +33 -0
- package/foundation/env/search-path.d.mts +42 -0
- package/foundation/env/search-path.mjs +205 -0
- package/foundation/env/search-path.test.mjs +122 -0
- package/foundation/env/user-dirs.d.mts +39 -0
- package/foundation/env/user-dirs.mjs +93 -0
- package/foundation/env/user-dirs.test.mjs +72 -0
- package/foundation/env/user-settings.d.mts +46 -0
- package/foundation/env/user-settings.mjs +106 -0
- package/foundation/env/user-settings.test.mjs +64 -0
- package/foundation/fs/file-protection.mjs +15 -1
- package/foundation/fs/module-loader.d.mts +2 -1
- package/foundation/fs/module-loader.mjs +60 -4
- package/foundation/fs/module-loader.stdout.test.mjs +3 -2
- package/foundation/fs/module-loader.test.mjs +194 -0
- package/foundation/fs/path-compare.d.mts +33 -0
- package/foundation/fs/path-compare.mjs +73 -0
- package/foundation/fs/paths.d.mts +57 -1
- package/foundation/fs/paths.mjs +131 -3
- package/foundation/fs/paths.test.mjs +10 -1
- package/foundation/integrations/computer-integrations.d.mts +78 -0
- package/foundation/integrations/computer-integrations.mjs +316 -0
- package/foundation/integrations/computer-integrations.test.mjs +292 -0
- package/foundation/integrations/contribution-inventory.d.mts +11 -0
- package/foundation/integrations/contribution-inventory.mjs +56 -3
- package/foundation/integrations/integration-warnings.mjs +3 -2
- package/foundation/integrations/integration-warnings.test.mjs +8 -9
- package/foundation/integrations/integrations.d.mts +25 -1
- package/foundation/integrations/integrations.mjs +46 -3
- package/foundation/integrations/issue.ts +6 -0
- package/foundation/integrations/validate-contributions.d.mts +2 -0
- package/foundation/integrations/validate-contributions.mjs +41 -1
- package/foundation/release/agent-text-worker.d.mts +4 -0
- package/foundation/release/agent-text-worker.mjs +52 -0
- package/foundation/release/cache.d.mts +310 -0
- package/foundation/release/cache.mjs +1216 -0
- package/foundation/release/cache.test.mjs +920 -0
- package/foundation/release/choose.d.mts +207 -0
- package/foundation/release/choose.mjs +677 -0
- package/foundation/release/choose.test.mjs +427 -0
- package/foundation/release/core-worker.d.mts +4 -0
- package/foundation/release/core-worker.mjs +38 -0
- package/foundation/release/global-state.d.mts +259 -0
- package/foundation/release/global-state.mjs +472 -0
- package/foundation/release/global-state.test.mjs +381 -0
- package/foundation/release/handoff.d.mts +60 -0
- package/foundation/release/handoff.e2e.test.mjs +278 -0
- package/foundation/release/handoff.mjs +357 -0
- package/foundation/release/lock-lease.d.mts +23 -0
- package/foundation/release/lock-lease.mjs +80 -0
- package/foundation/release/notices.d.mts +253 -0
- package/foundation/release/notices.mjs +596 -0
- package/foundation/release/notices.test.mjs +565 -0
- package/foundation/release/npm-guard.d.mts +4 -0
- package/foundation/release/npm-guard.mjs +126 -0
- package/foundation/release/project.d.mts +141 -0
- package/foundation/release/project.mjs +466 -0
- package/foundation/release/project.test.mjs +288 -0
- package/foundation/release/refresh-worker.d.mts +4 -0
- package/foundation/release/refresh-worker.mjs +27 -0
- package/foundation/release/run-context.d.mts +47 -0
- package/foundation/release/run-context.mjs +149 -0
- package/foundation/release/update-notice-env.d.mts +28 -0
- package/foundation/release/update-notice-env.mjs +39 -0
- package/foundation/response/base.d.ts +26 -3
- package/foundation/response/error-codes.d.mts +3 -1
- package/foundation/response/error-codes.doc.mjs +12 -0
- package/foundation/response/error-codes.mjs +17 -0
- package/foundation/response/error-codes.test.mjs +2 -0
- package/foundation/response/json-contract.test.mjs +7 -2
- package/foundation/response/json.mjs +4 -1
- package/foundation/response/response-types.doc.mjs +22 -11
- package/foundation/response/response-types.doc.test.mjs +46 -0
- package/foundation/response/response.doc.mjs +3 -3
- package/foundation/xle/browser-imports.test.mjs +74 -0
- package/foundation/xle/registry.mjs +4 -0
- package/foundation/xle/registry.test.mjs +28 -0
- package/package.json +11 -11
- package/scripts/postinstall.mjs +24 -5
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Update notices (spec AST-081 FR10), the way `gh` and the Vercel CLI
|
|
5
|
+
* tell their users about a newer release: after the command finishes, on
|
|
6
|
+
* standard error, at most once a day per notice, never in machine-readable
|
|
7
|
+
* output, and never when the environment turns them off (`CI`,
|
|
8
|
+
* `NO_UPDATE_NOTIFIER`; update-notice-env.mjs). Unlike `gh`, a notice prints
|
|
9
|
+
* when standard error is not a terminal too, because agents run the CLI
|
|
10
|
+
* without one.
|
|
11
|
+
*
|
|
12
|
+
* Each notice is two lines:
|
|
13
|
+
*
|
|
14
|
+
* A new release of Astryx is available: 0.7.2 → 0.7.3
|
|
15
|
+
* To upgrade, run: npm i -g @astryxdesign/cli@latest
|
|
16
|
+
*
|
|
17
|
+
* - Outside a project, about the global copy that answered, with the command
|
|
18
|
+
* of the package manager that installed it (npm, pnpm, Yarn, Bun, Volta),
|
|
19
|
+
* read from where it lives, as the Vercel CLI does.
|
|
20
|
+
* - Inside a project, about the project's release, with the project's install
|
|
21
|
+
* and `upgrade` commands.
|
|
22
|
+
* - In any folder, about an `astryx` command first on the search path that
|
|
23
|
+
* predates the hand-off and so answers every folder itself.
|
|
24
|
+
*
|
|
25
|
+
* A newer release needs a check that has reached the registry
|
|
26
|
+
* (global-state.mjs); the last kind needs none. When the newest release needs
|
|
27
|
+
* a newer Node than the computer runs, the second line says so. Prerelease and
|
|
28
|
+
* canary versions are never reported as behind. The times of the notices are
|
|
29
|
+
* kept in the per-user state file; a time that cannot be recorded keeps the
|
|
30
|
+
* notice from printing, so it never prints on every run.
|
|
31
|
+
*
|
|
32
|
+
* @input the per-user state, the release choice, the project's files, the
|
|
33
|
+
* search path, the clock, and the environment
|
|
34
|
+
* @output notice lines, and the facts doctor reports
|
|
35
|
+
* @position foundation/release — printed by handoff.mjs's startRun after the
|
|
36
|
+
* command, read by doctor.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import * as fs from 'node:fs';
|
|
40
|
+
import * as os from 'node:os';
|
|
41
|
+
import * as path from 'node:path';
|
|
42
|
+
import {userConfigDir} from '../env/user-dirs.mjs';
|
|
43
|
+
import {astryxCommandsOnSearchPath} from '../env/search-path.mjs';
|
|
44
|
+
import {
|
|
45
|
+
CLI_PACKAGE,
|
|
46
|
+
detectPackageManager,
|
|
47
|
+
getCliInvocation,
|
|
48
|
+
} from '../env/package-manager.mjs';
|
|
49
|
+
import {satisfiesRange, semverCompare} from '../env/semver.mjs';
|
|
50
|
+
import {isInReleaseCache, isReleaseVersion} from './cache.mjs';
|
|
51
|
+
import {
|
|
52
|
+
knownLatest,
|
|
53
|
+
readGlobalState,
|
|
54
|
+
stateFile,
|
|
55
|
+
tryStateLock,
|
|
56
|
+
writeGlobalState,
|
|
57
|
+
} from './global-state.mjs';
|
|
58
|
+
import {globalFolderOf, isGlobalCopy, projectRootOf} from './choose.mjs';
|
|
59
|
+
import {readReleaseFacts} from './project.mjs';
|
|
60
|
+
import {samePath} from '../fs/path-compare.mjs';
|
|
61
|
+
import {updateNoticesOff} from './update-notice-env.mjs';
|
|
62
|
+
|
|
63
|
+
/** A notice prints at most once in this long. */
|
|
64
|
+
export const NOTICE_INTERVAL_MS = 24 * 60 * 60_000;
|
|
65
|
+
|
|
66
|
+
/** Notice times older than this are dropped from the state file. */
|
|
67
|
+
const FORGET_AFTER_MS = 30 * 24 * 60 * 60_000;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The CLI package at its newest release, for every upgrade command. Spelled
|
|
71
|
+
* out, not built from package-manager.mjs's constant: that module reaches
|
|
72
|
+
* this one through the run context, so its bindings are not ready when this
|
|
73
|
+
* module loads.
|
|
74
|
+
*/
|
|
75
|
+
const LATEST = '@astryxdesign/cli@latest';
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Whether notice `key` may print now; when it may, its time is recorded
|
|
79
|
+
* first, and a time that cannot be recorded keeps it from printing.
|
|
80
|
+
* @param {string} key
|
|
81
|
+
* @param {{stateDir?: string, now?: number}} [options]
|
|
82
|
+
* @returns {boolean}
|
|
83
|
+
*/
|
|
84
|
+
export function takeNotice(
|
|
85
|
+
key,
|
|
86
|
+
{stateDir = userConfigDir(), now = Date.now()} = {},
|
|
87
|
+
) {
|
|
88
|
+
try {
|
|
89
|
+
fs.mkdirSync(stateDir, {recursive: true});
|
|
90
|
+
} catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
// Read, decide, and record under one lock: of the runs that finish at the
|
|
94
|
+
// same moment, exactly one takes the notice.
|
|
95
|
+
const outcome = tryStateLock(stateDir, () => {
|
|
96
|
+
const state = readGlobalState(stateDir);
|
|
97
|
+
const shown = state.notifiedAt ?? {};
|
|
98
|
+
const last = shown[key];
|
|
99
|
+
if (typeof last === 'number' && now - last < NOTICE_INTERVAL_MS) {
|
|
100
|
+
const skips = withSkip(state.noticeSkips, key, 'already-shown', now);
|
|
101
|
+
if (skips) writeGlobalState({...state, noticeSkips: skips}, stateDir);
|
|
102
|
+
return /** @type {const} */ ('already-shown');
|
|
103
|
+
}
|
|
104
|
+
/** @type {Record<string, number>} */
|
|
105
|
+
const next = {};
|
|
106
|
+
for (const [k, t] of Object.entries(shown)) {
|
|
107
|
+
if (typeof t === 'number' && now - t < FORGET_AFTER_MS) next[k] = t;
|
|
108
|
+
}
|
|
109
|
+
next[key] = now;
|
|
110
|
+
return writeGlobalState({...state, notifiedAt: next}, stateDir)
|
|
111
|
+
? /** @type {const} */ ('taken')
|
|
112
|
+
: /** @type {const} */ ('state-unwritable');
|
|
113
|
+
});
|
|
114
|
+
// A skip is recorded only inside the lock that made the decision. A lock
|
|
115
|
+
// held past the wait, or a state file that cannot be written, records
|
|
116
|
+
// nothing: doctor names a lock's holder from the lock itself.
|
|
117
|
+
return outcome.ran && outcome.value === 'taken';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** A skip with the same reason is recorded again only after this long. */
|
|
121
|
+
const SKIP_RECORD_INTERVAL_MS = 60 * 60_000;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* `skips` with `key`'s skip set to `reason` at `now`, dropping skips older
|
|
125
|
+
* than {@link FORGET_AFTER_MS}; null when the same reason was recorded within
|
|
126
|
+
* {@link SKIP_RECORD_INTERVAL_MS}, so a notice skipped on every run does not
|
|
127
|
+
* rewrite the state file on every run.
|
|
128
|
+
* @param {Record<string, import('./global-state.mjs').NoticeSkip> | undefined} skips
|
|
129
|
+
* @param {string} key
|
|
130
|
+
* @param {import('./global-state.mjs').NoticeSkip['reason']} reason
|
|
131
|
+
* @param {number} now
|
|
132
|
+
* @returns {Record<string, import('./global-state.mjs').NoticeSkip> | null}
|
|
133
|
+
*/
|
|
134
|
+
function withSkip(skips, key, reason, now) {
|
|
135
|
+
const last = skips?.[key];
|
|
136
|
+
if (last?.reason === reason && now - last.at < SKIP_RECORD_INTERVAL_MS) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
/** @type {Record<string, import('./global-state.mjs').NoticeSkip>} */
|
|
140
|
+
const next = {};
|
|
141
|
+
for (const [k, skip] of Object.entries(skips ?? {})) {
|
|
142
|
+
if (
|
|
143
|
+
skip &&
|
|
144
|
+
typeof skip.at === 'number' &&
|
|
145
|
+
now - skip.at < FORGET_AFTER_MS
|
|
146
|
+
) {
|
|
147
|
+
next[k] = skip;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
next[key] = {reason, at: now};
|
|
151
|
+
return next;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Record why notice `key` did not print (state `noticeSkips`), for diagnosis
|
|
156
|
+
* only. It adds to a state file that already exists and never creates one, so
|
|
157
|
+
* a run that keeps no state (a project's own CLI before any check) writes
|
|
158
|
+
* nothing. It is written only while this run holds the state file's lock:
|
|
159
|
+
* when the lock stays taken or the file cannot be written, nothing is
|
|
160
|
+
* recorded, and a run never fails because of it.
|
|
161
|
+
* @param {string} key
|
|
162
|
+
* @param {import('./global-state.mjs').NoticeSkip['reason']} reason
|
|
163
|
+
* @param {{stateDir?: string, now?: number}} [options]
|
|
164
|
+
*/
|
|
165
|
+
export function recordNoticeSkip(
|
|
166
|
+
key,
|
|
167
|
+
reason,
|
|
168
|
+
{stateDir = userConfigDir(), now = Date.now()} = {},
|
|
169
|
+
) {
|
|
170
|
+
try {
|
|
171
|
+
if (!fs.existsSync(stateFile(stateDir))) return;
|
|
172
|
+
tryStateLock(stateDir, () => {
|
|
173
|
+
const state = readGlobalState(stateDir);
|
|
174
|
+
const skips = withSkip(state.noticeSkips, key, reason, now);
|
|
175
|
+
if (skips) writeGlobalState({...state, noticeSkips: skips}, stateDir);
|
|
176
|
+
});
|
|
177
|
+
} catch {
|
|
178
|
+
// Diagnosis only.
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The newest notice skip the state file records, with the notice it was for.
|
|
184
|
+
* @param {{stateDir?: string}} [options]
|
|
185
|
+
* @returns {({key: string} & import('./global-state.mjs').NoticeSkip) | null}
|
|
186
|
+
*/
|
|
187
|
+
export function lastNoticeSkip({stateDir = userConfigDir()} = {}) {
|
|
188
|
+
/** @type {({key: string} & import('./global-state.mjs').NoticeSkip) | null} */
|
|
189
|
+
let newest = null;
|
|
190
|
+
for (const [key, skip] of Object.entries(
|
|
191
|
+
readGlobalState(stateDir).noticeSkips ?? {},
|
|
192
|
+
)) {
|
|
193
|
+
if (
|
|
194
|
+
skip &&
|
|
195
|
+
typeof skip.at === 'number' &&
|
|
196
|
+
(!newest || skip.at > newest.at)
|
|
197
|
+
) {
|
|
198
|
+
newest = {key, ...skip};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return newest;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Is `version` a plain release older than `newest`?
|
|
206
|
+
* @param {string | null | undefined} version
|
|
207
|
+
* @param {string | null | undefined} newest
|
|
208
|
+
* @returns {boolean}
|
|
209
|
+
*/
|
|
210
|
+
function isBehind(version, newest) {
|
|
211
|
+
return (
|
|
212
|
+
isReleaseVersion(version) &&
|
|
213
|
+
isReleaseVersion(newest) &&
|
|
214
|
+
!version.includes('-') &&
|
|
215
|
+
!newest.includes('-') &&
|
|
216
|
+
semverCompare(newest, version) > 0
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* The command that upgrades a CLI installed for the computer, by the package
|
|
222
|
+
* manager whose global folder holds it (the Vercel CLI reads it the same
|
|
223
|
+
* way); npm when none is recognized. A CLI that npm installed under a prefix
|
|
224
|
+
* of its own (`npm i -g --prefix ~/.local`, `NPM_CONFIG_PREFIX`, or `prefix=`
|
|
225
|
+
* in an `.npmrc`) is upgraded into that same prefix, named with `--prefix`,
|
|
226
|
+
* so the upgrade lands where the running copy is whatever npm's settings are
|
|
227
|
+
* when it runs. pnpm, Yarn, and Bun read their global folder from their own
|
|
228
|
+
* settings, so their plain command reinstalls in place.
|
|
229
|
+
* @param {string} cliRoot the CLI package's folder
|
|
230
|
+
* @param {{execPath?: string, home?: string, platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv}} [where]
|
|
231
|
+
* @returns {string}
|
|
232
|
+
*/
|
|
233
|
+
export function globalUpgradeCommand(cliRoot, where = {}) {
|
|
234
|
+
let at = cliRoot;
|
|
235
|
+
try {
|
|
236
|
+
at = fs.realpathSync(cliRoot);
|
|
237
|
+
} catch {
|
|
238
|
+
// Read the path as given.
|
|
239
|
+
}
|
|
240
|
+
if (/[\\/]\.volta[\\/]/iu.test(at)) return `volta install ${LATEST}`;
|
|
241
|
+
const {
|
|
242
|
+
home = os.homedir(),
|
|
243
|
+
platform = process.platform,
|
|
244
|
+
env = process.env,
|
|
245
|
+
} = where;
|
|
246
|
+
const global = globalFolderOf(at, {home, platform, env});
|
|
247
|
+
// Each reads the same settings that named its global folder, so its plain
|
|
248
|
+
// command reinstalls into that folder.
|
|
249
|
+
if (global?.manager === 'bun') return `bun add -g ${LATEST}`;
|
|
250
|
+
if (global?.manager === 'yarn') return `yarn global add ${LATEST}`;
|
|
251
|
+
if (
|
|
252
|
+
global?.manager === 'pnpm' ||
|
|
253
|
+
/[\\/]pnpm[\\/]|[\\/]global[\\/]v?\d+[\\/]/iu.test(at)
|
|
254
|
+
) {
|
|
255
|
+
return `pnpm add -g ${LATEST}`;
|
|
256
|
+
}
|
|
257
|
+
const prefix = npmOwnPrefix(at, where);
|
|
258
|
+
return prefix
|
|
259
|
+
? `npm i -g --prefix ${prefix} ${LATEST}`
|
|
260
|
+
: `npm i -g ${LATEST}`;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* The npm prefix that holds the CLI at `at`, as a person types it (`~` for
|
|
265
|
+
* the home folder outside Windows), when it is not one npm uses by default:
|
|
266
|
+
* Node's own prefix, `/usr/local` or `/usr`, or `%APPDATA%\\npm` on Windows.
|
|
267
|
+
* Null for any other layout.
|
|
268
|
+
* @param {string} at the CLI package's real path
|
|
269
|
+
* @param {{execPath?: string, home?: string, platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv}} where
|
|
270
|
+
* @returns {string | null}
|
|
271
|
+
*/
|
|
272
|
+
function npmOwnPrefix(at, where) {
|
|
273
|
+
const {
|
|
274
|
+
execPath = process.execPath,
|
|
275
|
+
home = os.homedir(),
|
|
276
|
+
platform = process.platform,
|
|
277
|
+
env = process.env,
|
|
278
|
+
} = where;
|
|
279
|
+
const win = platform === 'win32';
|
|
280
|
+
const paths = win ? path.win32 : path.posix;
|
|
281
|
+
const parts = at.split(/[\\/]/);
|
|
282
|
+
const n = parts.length;
|
|
283
|
+
if (parts[n - 1] !== 'cli' || parts[n - 2] !== '@astryxdesign') return null;
|
|
284
|
+
if (parts[n - 3] !== 'node_modules') return null;
|
|
285
|
+
// npm's global folder: `<prefix>/lib/node_modules` on macOS and Linux,
|
|
286
|
+
// `<prefix>\\node_modules` on Windows.
|
|
287
|
+
const depth = win ? n - 3 : n - 4;
|
|
288
|
+
if (!win && parts[n - 4] !== 'lib') return null;
|
|
289
|
+
const prefix = parts.slice(0, depth).join(win ? '\\' : '/') || paths.sep;
|
|
290
|
+
const defaults = win
|
|
291
|
+
? [
|
|
292
|
+
paths.dirname(execPath),
|
|
293
|
+
...(env.APPDATA ? [paths.join(env.APPDATA, 'npm')] : []),
|
|
294
|
+
]
|
|
295
|
+
: [paths.dirname(paths.dirname(execPath)), '/usr/local', '/usr'];
|
|
296
|
+
if (defaults.some(d => samePath(d, prefix, platform))) return null;
|
|
297
|
+
if (!win) {
|
|
298
|
+
const rel = paths.relative(home, prefix);
|
|
299
|
+
if (rel === '') return '~';
|
|
300
|
+
if (!rel.startsWith('..') && !paths.isAbsolute(rel)) return `~/${rel}`;
|
|
301
|
+
}
|
|
302
|
+
return /\s/.test(prefix) ? JSON.stringify(prefix) : prefix;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* The second line of a notice: the command, after the Node it needs when the
|
|
307
|
+
* computer runs an older one.
|
|
308
|
+
* @param {string} command
|
|
309
|
+
* @param {string | null} node the Node range the newest release declares
|
|
310
|
+
* @param {string} [nodeVersion]
|
|
311
|
+
* @returns {string}
|
|
312
|
+
*/
|
|
313
|
+
export function upgradeLine(
|
|
314
|
+
command,
|
|
315
|
+
node,
|
|
316
|
+
nodeVersion = process.versions.node,
|
|
317
|
+
) {
|
|
318
|
+
return node && !satisfiesRange(nodeVersion, node)
|
|
319
|
+
? `To upgrade, install Node ${node} (this computer runs Node ${nodeVersion}), then run: ${command}`
|
|
320
|
+
: `To upgrade, run: ${command}`;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* The first line of a newer-release notice.
|
|
325
|
+
* @param {string} current
|
|
326
|
+
* @param {string} newest
|
|
327
|
+
* @returns {string}
|
|
328
|
+
*/
|
|
329
|
+
export function availableLine(current, newest) {
|
|
330
|
+
return `A new release of Astryx is available: ${current} → ${newest}`;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* @typedef {object} GlobalBehind
|
|
335
|
+
* @property {string} current the global copy's version
|
|
336
|
+
* @property {string} newest the newest release known
|
|
337
|
+
* @property {string | null} node the Node range it declares
|
|
338
|
+
* @property {string} command the command that upgrades the global copy
|
|
339
|
+
*/
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Whether the global copy is older than the newest release known.
|
|
343
|
+
* @param {{self: {version: string, root: string}, stateDir?: string}} options
|
|
344
|
+
* @returns {GlobalBehind | null}
|
|
345
|
+
*/
|
|
346
|
+
export function globalBehind({self, stateDir = userConfigDir()}) {
|
|
347
|
+
const latest = knownLatest(stateDir);
|
|
348
|
+
if (!latest || !isBehind(self.version, latest.version)) return null;
|
|
349
|
+
return {
|
|
350
|
+
current: self.version,
|
|
351
|
+
newest: latest.version,
|
|
352
|
+
node: latest.node,
|
|
353
|
+
command: globalUpgradeCommand(self.root),
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* @typedef {object} ProjectLag
|
|
359
|
+
* @property {string} root the project's folder
|
|
360
|
+
* @property {string} current the release that answers it
|
|
361
|
+
* @property {string} newest the newest release known
|
|
362
|
+
* @property {string | null} node the Node range the newest release declares
|
|
363
|
+
* @property {string} install the command that installs the newest release
|
|
364
|
+
* @property {string} upgrade the command that migrates the project
|
|
365
|
+
*/
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* The install verb of each package manager.
|
|
369
|
+
* @type {Record<string, string>}
|
|
370
|
+
*/
|
|
371
|
+
const ADD = {
|
|
372
|
+
npm: 'npm install',
|
|
373
|
+
pnpm: 'pnpm add',
|
|
374
|
+
yarn: 'yarn add',
|
|
375
|
+
bun: 'bun add',
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* The commands that bring a project's own CLI and Core to one release, with
|
|
380
|
+
* the project's package manager: when the CLI is newer, Core up to it and then
|
|
381
|
+
* the codemods from Core's release; otherwise the CLI to Core's release.
|
|
382
|
+
* @param {{cwd: string, cli: string, core: string}} options
|
|
383
|
+
* @returns {string}
|
|
384
|
+
*/
|
|
385
|
+
export function alignCliAndCore({cwd, cli, core}) {
|
|
386
|
+
const facts = readReleaseFacts(cwd);
|
|
387
|
+
const root = projectRootOf(facts) ?? cwd;
|
|
388
|
+
const add = ADD[detectPackageManager(root)] ?? ADD.npm;
|
|
389
|
+
if (semverCompare(cli, core) > 0) {
|
|
390
|
+
return `${add} @astryxdesign/core@${cli}, then ${getCliInvocation(root, {installed: true})} upgrade --from ${core} --apply`;
|
|
391
|
+
}
|
|
392
|
+
return `${add} ${CLI_PACKAGE}@${core}`;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* The hint for a project with no CLI of its own, answered by `answered` while
|
|
397
|
+
* its Core is `core`: the command that adds the CLI at Core's version, with
|
|
398
|
+
* the project's package manager.
|
|
399
|
+
* @param {{cwd: string, answered: string, core: string}} options
|
|
400
|
+
* @returns {{root: string, line: string}}
|
|
401
|
+
*/
|
|
402
|
+
export function noCliHint({cwd, answered, core}) {
|
|
403
|
+
const root = projectRootOf(readReleaseFacts(cwd)) ?? cwd;
|
|
404
|
+
return {root, line: noCliHintLine(answered, core, addCliCommand(root, core))};
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* The hint's text.
|
|
409
|
+
* @param {string} answered
|
|
410
|
+
* @param {string} core
|
|
411
|
+
* @param {string} command the command that adds the CLI at Core's version
|
|
412
|
+
* @returns {string}
|
|
413
|
+
*/
|
|
414
|
+
export function noCliHintLine(answered, core, command) {
|
|
415
|
+
return `astryx: this project has no Astryx CLI installed; answered by CLI ${answered} while its Core is ${core}. To match, run: ${command}`;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* The command that installs the CLI at `version` in the project at `root`,
|
|
420
|
+
* with its package manager.
|
|
421
|
+
* @param {string} root
|
|
422
|
+
* @param {string} version
|
|
423
|
+
* @returns {string}
|
|
424
|
+
*/
|
|
425
|
+
export function addCliCommand(root, version) {
|
|
426
|
+
return `${ADD[detectPackageManager(root)] ?? ADD.npm} ${CLI_PACKAGE}@${version}`;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Whether the project's release is older than the newest release known, and
|
|
431
|
+
* the commands that upgrade it.
|
|
432
|
+
* @param {{cwd: string, current: string, stateDir?: string}} options
|
|
433
|
+
* @returns {ProjectLag | null}
|
|
434
|
+
*/
|
|
435
|
+
export function projectLag({cwd, current, stateDir = userConfigDir()}) {
|
|
436
|
+
const latest = knownLatest(stateDir);
|
|
437
|
+
if (!latest || !isBehind(current, latest.version)) return null;
|
|
438
|
+
const newest = latest.version;
|
|
439
|
+
const facts = readReleaseFacts(cwd);
|
|
440
|
+
if (!facts.inProject) return null;
|
|
441
|
+
const root = projectRootOf(facts) ?? cwd;
|
|
442
|
+
const pm = detectPackageManager(root);
|
|
443
|
+
// The CLI is always installed beside Core: a project that installed only
|
|
444
|
+
// Core gets it too, as recommended, so its version is pinned with Core's.
|
|
445
|
+
const install = `${ADD[pm] ?? ADD.npm} @astryxdesign/core@${newest} ${CLI_PACKAGE}@${newest}`;
|
|
446
|
+
// The command runs after the install above, so the picker's guess is the
|
|
447
|
+
// project's own CLI (spec AST-081 FR5).
|
|
448
|
+
const stem = getCliInvocation(root, {installed: true});
|
|
449
|
+
return {
|
|
450
|
+
root,
|
|
451
|
+
current,
|
|
452
|
+
newest,
|
|
453
|
+
node: latest.node,
|
|
454
|
+
install,
|
|
455
|
+
upgrade: `${stem} upgrade --from ${current} --apply`,
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* The first `astryx` on the search path, when it is an Astryx CLI release
|
|
461
|
+
* that predates the hand-off and is not the running copy.
|
|
462
|
+
* @param {{selfRoot?: string, searchPath?: string, platform?: NodeJS.Platform}} [options]
|
|
463
|
+
* @returns {{file: string, version: string | null, command: string} | null}
|
|
464
|
+
*/
|
|
465
|
+
export function oldGlobalCommand({selfRoot, ...options} = {}) {
|
|
466
|
+
const first = astryxCommandsOnSearchPath(options)[0];
|
|
467
|
+
if (!first?.astryx || !first.cliRoot || first.handsOff !== false) return null;
|
|
468
|
+
if (selfRoot && samePath(real(selfRoot), real(first.cliRoot))) return null;
|
|
469
|
+
return {
|
|
470
|
+
file: first.file,
|
|
471
|
+
version: first.version,
|
|
472
|
+
command: globalUpgradeCommand(first.cliRoot),
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* @param {string} p
|
|
478
|
+
* @returns {string}
|
|
479
|
+
*/
|
|
480
|
+
function real(p) {
|
|
481
|
+
try {
|
|
482
|
+
return fs.realpathSync(p);
|
|
483
|
+
} catch {
|
|
484
|
+
return path.resolve(p);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* The two lines about a global copy that predates the hand-off.
|
|
490
|
+
* @param {{version: string | null, command: string}} old
|
|
491
|
+
* @returns {string[]}
|
|
492
|
+
*/
|
|
493
|
+
export function oldGlobalLines(old) {
|
|
494
|
+
return [
|
|
495
|
+
`The astryx command on this computer runs CLI ${old.version ?? 'of an unknown version'}, which answers every folder itself.`,
|
|
496
|
+
`To upgrade, run: ${old.command}`,
|
|
497
|
+
];
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* The notice lines to print after a command, each notice recorded as shown.
|
|
502
|
+
* Empty in machine-readable runs and when the environment turns notices off.
|
|
503
|
+
* Never throws.
|
|
504
|
+
* @param {{
|
|
505
|
+
* cwd: string,
|
|
506
|
+
* self: {version: string, root: string},
|
|
507
|
+
* choice: {inProject: boolean, version: string, copy?: string, coreVersion?: string | null},
|
|
508
|
+
* json?: boolean,
|
|
509
|
+
* stateDir?: string,
|
|
510
|
+
* cacheDir?: string,
|
|
511
|
+
* now?: number,
|
|
512
|
+
* env?: Record<string, string | undefined>,
|
|
513
|
+
* searchPath?: string,
|
|
514
|
+
* }} options
|
|
515
|
+
* @returns {string[]}
|
|
516
|
+
*/
|
|
517
|
+
export function noticeLines({
|
|
518
|
+
cwd,
|
|
519
|
+
self,
|
|
520
|
+
choice,
|
|
521
|
+
json = false,
|
|
522
|
+
stateDir = userConfigDir(),
|
|
523
|
+
cacheDir,
|
|
524
|
+
now = Date.now(),
|
|
525
|
+
env = process.env,
|
|
526
|
+
searchPath,
|
|
527
|
+
}) {
|
|
528
|
+
// A run inside the release cache is the CLI checking a release it
|
|
529
|
+
// installed, never a person's run.
|
|
530
|
+
if (json || updateNoticesOff(env) || isInReleaseCache(cwd, cacheDir)) {
|
|
531
|
+
return [];
|
|
532
|
+
}
|
|
533
|
+
/** @type {string[]} */
|
|
534
|
+
const lines = [];
|
|
535
|
+
const take = (/** @type {string} */ key) => takeNotice(key, {stateDir, now});
|
|
536
|
+
try {
|
|
537
|
+
// A project with no CLI of its own, answered by a release other than its
|
|
538
|
+
// Core's: once a day per project, how to match them (FR2).
|
|
539
|
+
if (
|
|
540
|
+
choice.inProject &&
|
|
541
|
+
choice.copy === 'starting' &&
|
|
542
|
+
choice.coreVersion &&
|
|
543
|
+
choice.coreVersion !== choice.version
|
|
544
|
+
) {
|
|
545
|
+
const hint = noCliHint({
|
|
546
|
+
cwd,
|
|
547
|
+
answered: choice.version,
|
|
548
|
+
core: choice.coreVersion,
|
|
549
|
+
});
|
|
550
|
+
if (take(`no-cli:${hint.root}`)) lines.push(hint.line);
|
|
551
|
+
}
|
|
552
|
+
} catch {
|
|
553
|
+
// A hint never fails a run.
|
|
554
|
+
}
|
|
555
|
+
try {
|
|
556
|
+
if (choice.inProject) {
|
|
557
|
+
// The project's version is its own CLI's, or, with none, its Core's.
|
|
558
|
+
const current =
|
|
559
|
+
choice.copy === 'starting' && choice.coreVersion
|
|
560
|
+
? choice.coreVersion
|
|
561
|
+
: choice.version;
|
|
562
|
+
const lag = projectLag({cwd, current, stateDir});
|
|
563
|
+
if (!lag) recordNoticeSkip('project', 'no-newer', {stateDir, now});
|
|
564
|
+
if (lag && take(`project:${lag.root}`)) {
|
|
565
|
+
lines.push(
|
|
566
|
+
availableLine(lag.current, lag.newest),
|
|
567
|
+
upgradeLine(`${lag.install}, then ${lag.upgrade}`, lag.node),
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
} else if (isGlobalCopy(self.root, cacheDir)) {
|
|
571
|
+
const behind = globalBehind({self, stateDir});
|
|
572
|
+
if (!behind) recordNoticeSkip('global', 'no-newer', {stateDir, now});
|
|
573
|
+
if (behind && take('global')) {
|
|
574
|
+
lines.push(
|
|
575
|
+
availableLine(behind.current, behind.newest),
|
|
576
|
+
upgradeLine(behind.command, behind.node),
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
} catch {
|
|
581
|
+
// A notice never fails a run.
|
|
582
|
+
}
|
|
583
|
+
try {
|
|
584
|
+
const shown = readGlobalState(stateDir).notifiedAt?.['old-global'];
|
|
585
|
+
if (typeof shown !== 'number' || now - shown >= NOTICE_INTERVAL_MS) {
|
|
586
|
+
const old = oldGlobalCommand({
|
|
587
|
+
selfRoot: self.root,
|
|
588
|
+
...(searchPath === undefined ? {} : {searchPath}),
|
|
589
|
+
});
|
|
590
|
+
if (old && take('old-global')) lines.push(...oldGlobalLines(old));
|
|
591
|
+
}
|
|
592
|
+
} catch {
|
|
593
|
+
// Nor does this one.
|
|
594
|
+
}
|
|
595
|
+
return lines;
|
|
596
|
+
}
|