@omega.js/manager 0.1.0
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/.claude-plugin/marketplace.json +14 -0
- package/LICENSE +98 -0
- package/README.md +165 -0
- package/bin/omega +2 -0
- package/bin/omg +2 -0
- package/claude-plugin/.claude-plugin/plugin.json +11 -0
- package/claude-plugin/.mcp.json +9 -0
- package/claude-plugin/README.md +139 -0
- package/claude-plugin/hooks/gate/mark.sh +58 -0
- package/claude-plugin/hooks/gate/run.sh +97 -0
- package/claude-plugin/hooks/guard/run.sh +249 -0
- package/claude-plugin/hooks/hooks.json +63 -0
- package/claude-plugin/hooks/inject/run.sh +127 -0
- package/claude-plugin/hooks/lib/omega-gate.sh +20 -0
- package/claude-plugin/hooks/lib/omega-scope.sh +41 -0
- package/claude-plugin/hooks/lib/omega-skills.sh +209 -0
- package/claude-plugin/hooks/quality/run.sh +132 -0
- package/claude-plugin/hooks/shape/run.sh +45 -0
- package/claude-plugin/mcp-router-launch.js +29 -0
- package/claude-plugin/skills/README.md +48 -0
- package/claude-plugin/skills/accessibility/SKILL.md +31 -0
- package/claude-plugin/skills/analytics/SKILL.md +30 -0
- package/claude-plugin/skills/backend/SKILL.md +25 -0
- package/claude-plugin/skills/brandcheck/SKILL.md +29 -0
- package/claude-plugin/skills/browser/SKILL.md +64 -0
- package/claude-plugin/skills/client/SKILL.md +24 -0
- package/claude-plugin/skills/desktop/SKILL.md +25 -0
- package/claude-plugin/skills/extension/SKILL.md +25 -0
- package/claude-plugin/skills/main/SKILL.md +54 -0
- package/claude-plugin/skills/manager/SKILL.md +25 -0
- package/claude-plugin/skills/seo/SKILL.md +36 -0
- package/claude-plugin/skills/theme/SKILL.md +33 -0
- package/claude-plugin/skills/web/SKILL.md +29 -0
- package/dist/cli-run.js +34 -0
- package/dist/cli.js +29 -0
- package/dist/commands/build.js +17 -0
- package/dist/commands/clean.js +16 -0
- package/dist/commands/company.js +46 -0
- package/dist/commands/deploy.js +143 -0
- package/dist/commands/dev.js +437 -0
- package/dist/commands/devlog.js +25 -0
- package/dist/commands/help.js +48 -0
- package/dist/commands/manage.js +53 -0
- package/dist/commands/migrate.js +76 -0
- package/dist/commands/onboard.js +31 -0
- package/dist/commands/pipeline.js +319 -0
- package/dist/commands/test.js +380 -0
- package/dist/commands/update.js +128 -0
- package/dist/commands/version.js +8 -0
- package/dist/company-init.js +167 -0
- package/dist/company.js +283 -0
- package/dist/config.js +1359 -0
- package/dist/devlog/index.js +226 -0
- package/dist/devlog/lib/collect-commits.js +136 -0
- package/dist/devlog/lib/generate-post.js +201 -0
- package/dist/devlog/lib/ghostii.js +122 -0
- package/dist/devlog/lib/project-map.js +98 -0
- package/dist/devlog/lib/publish-website.js +88 -0
- package/dist/index.js +45 -0
- package/dist/lib/agents-md.js +201 -0
- package/dist/lib/analytics-secret.js +22 -0
- package/dist/lib/argv.js +21 -0
- package/dist/lib/auth-admin.js +120 -0
- package/dist/lib/automation-client.js +210 -0
- package/dist/lib/backend-marketing.js +49 -0
- package/dist/lib/brand.js +240 -0
- package/dist/lib/bundle-id.js +53 -0
- package/dist/lib/claude-settings.js +125 -0
- package/dist/lib/company-scaffold.js +213 -0
- package/dist/lib/company.js +227 -0
- package/dist/lib/config-flow.js +350 -0
- package/dist/lib/config-write.js +41 -0
- package/dist/lib/custom-target.js +75 -0
- package/dist/lib/domain-utils.js +19 -0
- package/dist/lib/duration.js +39 -0
- package/dist/lib/env-order.js +236 -0
- package/dist/lib/env-secret.js +78 -0
- package/dist/lib/firestore-rest.js +326 -0
- package/dist/lib/framework-bin.js +156 -0
- package/dist/lib/gitignore.js +63 -0
- package/dist/lib/google-auth.js +495 -0
- package/dist/lib/google-token.js +66 -0
- package/dist/lib/jwt.js +31 -0
- package/dist/lib/legacy-oauth.js +59 -0
- package/dist/lib/node-version.js +127 -0
- package/dist/lib/owner-plan.js +78 -0
- package/dist/lib/package-scripts.js +152 -0
- package/dist/lib/preflight.js +470 -0
- package/dist/lib/product-create.js +144 -0
- package/dist/lib/run-command.js +74 -0
- package/dist/lib/run-gates.js +70 -0
- package/dist/lib/run-output.js +42 -0
- package/dist/lib/run-summary.js +384 -0
- package/dist/lib/scaffold.js +427 -0
- package/dist/lib/service-input.js +207 -0
- package/dist/lib/service-runner.js +466 -0
- package/dist/lib/stale.js +24 -0
- package/dist/lib/target-selection.js +155 -0
- package/dist/lib/verb-fanout.js +128 -0
- package/dist/lib/verify-live.js +188 -0
- package/dist/manage.js +311 -0
- package/dist/omega-bin.js +7 -0
- package/dist/onboard.js +532 -0
- package/dist/services/account/ensure/users.js +259 -0
- package/dist/services/account/index.js +97 -0
- package/dist/services/account/lib/backend-client.js +107 -0
- package/dist/services/account/lib/password.js +36 -0
- package/dist/services/account/lib/resolve-password.js +100 -0
- package/dist/services/advertising/ensure/sites.js +101 -0
- package/dist/services/advertising/index.js +101 -0
- package/dist/services/advertising/lib/adsense-api.js +41 -0
- package/dist/services/ai/ensure/keys.js +21 -0
- package/dist/services/ai/index.js +36 -0
- package/dist/services/analytics/ensure/google-firebase-link.js +230 -0
- package/dist/services/analytics/ensure/google-streams.js +256 -0
- package/dist/services/analytics/ensure/meta-pixel.js +22 -0
- package/dist/services/analytics/ensure/tiktok-pixel.js +22 -0
- package/dist/services/analytics/index.js +134 -0
- package/dist/services/analytics/lib/analytics-api.js +156 -0
- package/dist/services/analytics/lib/meta-api.js +83 -0
- package/dist/services/analytics/lib/pixel-account.js +78 -0
- package/dist/services/analytics/lib/pixel-provision.js +152 -0
- package/dist/services/analytics/lib/pixel-specs.js +57 -0
- package/dist/services/analytics/lib/pixel-token.js +88 -0
- package/dist/services/analytics/lib/property-flow.js +77 -0
- package/dist/services/analytics/lib/tiktok-api.js +157 -0
- package/dist/services/analytics/lib/tiktok-auth.js +153 -0
- package/dist/services/assets/ensure/logo-gen.js +68 -0
- package/dist/services/assets/index.js +109 -0
- package/dist/services/assets/lib/assets-config.js +168 -0
- package/dist/services/assets/lib/brandmark-api.js +160 -0
- package/dist/services/assets/lib/derived.js +113 -0
- package/dist/services/assets/lib/font-loader.js +67 -0
- package/dist/services/assets/lib/reconcile.js +122 -0
- package/dist/services/assets/lib/reset.js +118 -0
- package/dist/services/assets/lib/svg-logo-generator.js +154 -0
- package/dist/services/assets/lib/svg-to-black.js +32 -0
- package/dist/services/assets/write/favicons.js +82 -0
- package/dist/services/assets/write/icons.js +64 -0
- package/dist/services/assets/write/process.js +96 -0
- package/dist/services/assets/write/reconcile.js +30 -0
- package/dist/services/assets/write/social-icons.js +108 -0
- package/dist/services/assets/write/templates.js +345 -0
- package/dist/services/bookmark/ensure/sync.js +255 -0
- package/dist/services/bookmark/index.js +17 -0
- package/dist/services/campaigns/ensure/contact-person.js +40 -0
- package/dist/services/campaigns/ensure/custom-fields.js +84 -0
- package/dist/services/campaigns/ensure/domain-auth.js +92 -0
- package/dist/services/campaigns/ensure/event-webhook.js +89 -0
- package/dist/services/campaigns/ensure/link-branding.js +187 -0
- package/dist/services/campaigns/ensure/list.js +50 -0
- package/dist/services/campaigns/ensure/segments.js +109 -0
- package/dist/services/campaigns/ensure/sender-identity.js +153 -0
- package/dist/services/campaigns/ensure/unsubscribe-groups.js +116 -0
- package/dist/services/campaigns/index.js +79 -0
- package/dist/services/campaigns/lib/dns-sync.js +81 -0
- package/dist/services/campaigns/lib/segment-query.js +138 -0
- package/dist/services/campaigns/lib/sendgrid-api.js +287 -0
- package/dist/services/captcha/ensure/site-key.js +83 -0
- package/dist/services/captcha/index.js +78 -0
- package/dist/services/captcha/lib/console-url.js +45 -0
- package/dist/services/captcha/lib/recaptcha-api.js +36 -0
- package/dist/services/certificates/ensure/api-key.js +22 -0
- package/dist/services/certificates/ensure/bundle-ids.js +115 -0
- package/dist/services/certificates/ensure/certificates.js +187 -0
- package/dist/services/certificates/ensure/profiles.js +143 -0
- package/dist/services/certificates/index.js +225 -0
- package/dist/services/certificates/lib/apple-api.js +183 -0
- package/dist/services/certificates/lib/certificate-manager.js +252 -0
- package/dist/services/certificates/lib/identifier-manager.js +107 -0
- package/dist/services/certificates/lib/keychain.js +135 -0
- package/dist/services/certificates/lib/manual-walkthrough.js +165 -0
- package/dist/services/certificates/lib/profile-manager.js +115 -0
- package/dist/services/chat/data/baseline-knowledge.md +96 -0
- package/dist/services/chat/ensure/chat.js +106 -0
- package/dist/services/chat/ensure/user.js +45 -0
- package/dist/services/chat/index.js +139 -0
- package/dist/services/chat/lib/baseline-knowledge.js +137 -0
- package/dist/services/cloud/ensure/authentication.js +267 -0
- package/dist/services/cloud/ensure/billing.js +120 -0
- package/dist/services/cloud/ensure/cloud-messaging.js +105 -0
- package/dist/services/cloud/ensure/database.js +60 -0
- package/dist/services/cloud/ensure/firestore.js +79 -0
- package/dist/services/cloud/ensure/functions.js +49 -0
- package/dist/services/cloud/ensure/hosting.js +398 -0
- package/dist/services/cloud/ensure/oauth-consent.js +248 -0
- package/dist/services/cloud/ensure/project-settings.js +72 -0
- package/dist/services/cloud/ensure/sdk-config.js +95 -0
- package/dist/services/cloud/ensure/service-account.js +152 -0
- package/dist/services/cloud/ensure/services.js +103 -0
- package/dist/services/cloud/ensure/storage.js +47 -0
- package/dist/services/cloud/index.js +130 -0
- package/dist/services/cloud/lib/access-heal.js +130 -0
- package/dist/services/cloud/lib/firebase-api.js +662 -0
- package/dist/services/cloud/lib/project-flow.js +102 -0
- package/dist/services/directory/ensure/entry.js +48 -0
- package/dist/services/directory/index.js +64 -0
- package/dist/services/directory/lib/blocks.js +104 -0
- package/dist/services/disperse/index.js +45 -0
- package/dist/services/disperse/write/certs.js +117 -0
- package/dist/services/domain/ensure/nameservers.js +179 -0
- package/dist/services/domain/index.js +69 -0
- package/dist/services/domain/lib/namecheap-api.js +167 -0
- package/dist/services/domain/lib/registrars.js +46 -0
- package/dist/services/domain/lib/whitelist-walkthrough.js +76 -0
- package/dist/services/edge/ensure/cache-rules.js +105 -0
- package/dist/services/edge/ensure/dns-records.js +304 -0
- package/dist/services/edge/ensure/email-routing.js +292 -0
- package/dist/services/edge/ensure/rules-configuration.js +98 -0
- package/dist/services/edge/ensure/rules-managed-transforms.js +101 -0
- package/dist/services/edge/ensure/rules-redirect.js +107 -0
- package/dist/services/edge/ensure/rules-response-headers.js +90 -0
- package/dist/services/edge/ensure/rules-security.js +142 -0
- package/dist/services/edge/ensure/speed-scheduled-tests.js +98 -0
- package/dist/services/edge/ensure/workers.js +219 -0
- package/dist/services/edge/ensure/zone-settings.js +108 -0
- package/dist/services/edge/ensure/zone.js +183 -0
- package/dist/services/edge/index.js +73 -0
- package/dist/services/edge/lib/cloudflare-api.js +84 -0
- package/dist/services/edge/lib/dns-records-helpers.js +518 -0
- package/dist/services/edge/lib/read-cache.js +16 -0
- package/dist/services/edge/lib/ruleset-helper.js +108 -0
- package/dist/services/edge/workers/omega-api-proxy.js +43 -0
- package/dist/services/email/data/baseline-filter.md +4 -0
- package/dist/services/email/data/baseline-knowledge.md +86 -0
- package/dist/services/email/ensure/agent.js +107 -0
- package/dist/services/email/ensure/user.js +45 -0
- package/dist/services/email/index.js +138 -0
- package/dist/services/email/lib/baseline.js +82 -0
- package/dist/services/email/lib/knowledge-file.js +53 -0
- package/dist/services/forms/ensure/form.js +46 -0
- package/dist/services/forms/ensure/user.js +45 -0
- package/dist/services/forms/index.js +138 -0
- package/dist/services/migrations/ensure/notifications.js +189 -0
- package/dist/services/migrations/ensure/orders.js +34 -0
- package/dist/services/migrations/ensure/payment-provider.js +132 -0
- package/dist/services/migrations/ensure/payments-intents.js +34 -0
- package/dist/services/migrations/ensure/state-retirement.js +266 -0
- package/dist/services/migrations/ensure/targets-rename.js +124 -0
- package/dist/services/migrations/ensure/users.js +1176 -0
- package/dist/services/migrations/index.js +101 -0
- package/dist/services/migrations/lib/attribution-touch.js +64 -0
- package/dist/services/migrations/lib/ensure-metadata.js +144 -0
- package/dist/services/migrations/lib/migration-runner.js +473 -0
- package/dist/services/migrations/lib/sanitize-strings.js +112 -0
- package/dist/services/migrations/lib/schema-validator.js +154 -0
- package/dist/services/monitoring/ensure/dsn.js +54 -0
- package/dist/services/monitoring/ensure/projects.js +135 -0
- package/dist/services/monitoring/index.js +54 -0
- package/dist/services/monitoring/lib/sentry-api.js +94 -0
- package/dist/services/newsletter/ensure/custom-fields.js +90 -0
- package/dist/services/newsletter/ensure/publication.js +94 -0
- package/dist/services/newsletter/ensure/segments.js +140 -0
- package/dist/services/newsletter/ensure/webhook.js +108 -0
- package/dist/services/newsletter/index.js +64 -0
- package/dist/services/newsletter/lib/beehiiv-api.js +124 -0
- package/dist/services/newsletter/lib/segment-automation.js +577 -0
- package/dist/services/payment/ensure/chargebee-account.js +30 -0
- package/dist/services/payment/ensure/chargebee-products.js +359 -0
- package/dist/services/payment/ensure/chargebee-webhook.js +113 -0
- package/dist/services/payment/ensure/paypal-account.js +36 -0
- package/dist/services/payment/ensure/paypal-products.js +374 -0
- package/dist/services/payment/ensure/paypal-webhook.js +128 -0
- package/dist/services/payment/ensure/stripe-account.js +93 -0
- package/dist/services/payment/ensure/stripe-disputes.js +56 -0
- package/dist/services/payment/ensure/stripe-products.js +245 -0
- package/dist/services/payment/ensure/stripe-radar.js +74 -0
- package/dist/services/payment/ensure/stripe-webhook.js +130 -0
- package/dist/services/payment/index.js +163 -0
- package/dist/services/payment/lib/chargebee-api.js +338 -0
- package/dist/services/payment/lib/payment-utils.js +116 -0
- package/dist/services/payment/lib/paypal-api.js +354 -0
- package/dist/services/payment/lib/provider-setup.js +134 -0
- package/dist/services/payment/lib/stripe-api.js +152 -0
- package/dist/services/repo/ensure/org.js +70 -0
- package/dist/services/repo/ensure/pages.js +72 -0
- package/dist/services/repo/ensure/repo.js +90 -0
- package/dist/services/repo/index.js +52 -0
- package/dist/services/repo/lib/github-api.js +196 -0
- package/dist/services/search/ensure/ga-link.js +57 -0
- package/dist/services/search/ensure/property.js +137 -0
- package/dist/services/search/ensure/sitemaps.js +70 -0
- package/dist/services/search/index.js +61 -0
- package/dist/services/search/lib/search-console-api.js +92 -0
- package/dist/services/seo/ensure/github-repos.js +271 -0
- package/dist/services/seo/index.js +55 -0
- package/dist/services/seo/lib/gh-api.js +161 -0
- package/dist/services/seo/templates/developer-tool/.github/workflows/maintenance.yml +25 -0
- package/dist/services/seo/templates/developer-tool/.nvmrc +1 -0
- package/dist/services/seo/templates/developer-tool/_README.md.js +121 -0
- package/dist/services/seo/templates/developer-tool/_package.json.js +33 -0
- package/dist/services/seo/templates/developer-tool/src/index.js +120 -0
- package/dist/services/seo/templates/index.js +57 -0
- package/dist/services/server/ensure/brands.js +63 -0
- package/dist/services/server/index.js +42 -0
- package/dist/services/testing/ensure/target-checks.js +97 -0
- package/dist/services/testing/index.js +11 -0
- package/dist/services/testing/lib/checks.js +467 -0
- package/dist/services/update/index.js +20 -0
- package/dist/services/update/lib/cache.js +59 -0
- package/dist/services/update/lib/fingerprint.js +157 -0
- package/dist/services/update/write/targets.js +206 -0
- package/dist/services/workspace/ensure/agents.js +42 -0
- package/dist/services/workspace/ensure/claude-settings.js +34 -0
- package/dist/services/workspace/ensure/config.js +54 -0
- package/dist/services/workspace/ensure/defaults.js +60 -0
- package/dist/services/workspace/ensure/env-keys.js +55 -0
- package/dist/services/workspace/ensure/env-order.js +66 -0
- package/dist/services/workspace/ensure/env-rules.js +97 -0
- package/dist/services/workspace/ensure/gitignore.js +18 -0
- package/dist/services/workspace/ensure/scripts.js +126 -0
- package/dist/services/workspace/ensure/structure.js +97 -0
- package/dist/services/workspace/ensure/translation-sdk.js +148 -0
- package/dist/services/workspace/ensure/workflows.js +54 -0
- package/dist/services/workspace/index.js +8 -0
- package/dist/vendor/account/engine.js +182 -0
- package/dist/vendor/account/features.js +220 -0
- package/dist/vendor/account/index.js +53 -0
- package/dist/vendor/account/schema.js +272 -0
- package/dist/vendor/account/subscription.js +38 -0
- package/dist/vendor/config/company.js +31 -0
- package/dist/vendor/config/defaults.js +173 -0
- package/dist/vendor/config/demo.js +18 -0
- package/dist/vendor/config/desktop-artifacts.js +110 -0
- package/dist/vendor/config/edit.js +769 -0
- package/dist/vendor/config/env-delivery.js +145 -0
- package/dist/vendor/config/env-rules.js +93 -0
- package/dist/vendor/config/env-schema.js +1078 -0
- package/dist/vendor/config/env.js +445 -0
- package/dist/vendor/config/hooks.js +97 -0
- package/dist/vendor/config/index.js +237 -0
- package/dist/vendor/config/instances.js +208 -0
- package/dist/vendor/config/load.js +490 -0
- package/dist/vendor/config/merge.js +68 -0
- package/dist/vendor/config/order.js +139 -0
- package/dist/vendor/config/ports.js +374 -0
- package/dist/vendor/config/providers.js +32 -0
- package/dist/vendor/config/repo.js +142 -0
- package/dist/vendor/config/retired-keys.js +430 -0
- package/dist/vendor/config/schema.js +1610 -0
- package/dist/vendor/config/secrets.js +50 -0
- package/dist/vendor/config/seed.js +34 -0
- package/dist/vendor/config/site-global.js +205 -0
- package/dist/vendor/config/validate.js +554 -0
- package/dist/vendor/config/winback.js +61 -0
- package/dist/vendor/devkit/attach-log-file.js +262 -0
- package/dist/vendor/devkit/certs.js +199 -0
- package/dist/vendor/devkit/ci-workflows.js +520 -0
- package/dist/vendor/devkit/cli-router.js +156 -0
- package/dist/vendor/devkit/command-path.js +46 -0
- package/dist/vendor/devkit/deploy-record.js +180 -0
- package/dist/vendor/devkit/flows.js +317 -0
- package/dist/vendor/devkit/local-https.js +360 -0
- package/dist/vendor/devkit/local.js +1905 -0
- package/dist/vendor/devkit/logger.js +128 -0
- package/dist/vendor/devkit/omega-bin.js +345 -0
- package/dist/vendor/devkit/prompt.js +187 -0
- package/dist/vendor/devkit/safe-install.js +18 -0
- package/dist/vendor/devkit/stop-signals.js +28 -0
- package/dist/vendor/devkit/test/scope.js +162 -0
- package/dist/vendor/devkit/translate/cache.js +84 -0
- package/dist/vendor/devkit/translate/engine.js +243 -0
- package/dist/vendor/devkit/translate/index.js +49 -0
- package/dist/vendor/devkit/translate/languages.js +134 -0
- package/dist/vendor/devkit/translate/providers.js +188 -0
- package/dist/vendor/devkit/update.js +569 -0
- package/docs/AGENTS.md +200 -0
- package/docs/account.md +45 -0
- package/docs/advertising.md +46 -0
- package/docs/ai.md +34 -0
- package/docs/analytics.md +69 -0
- package/docs/assets.md +57 -0
- package/docs/bookmark.md +26 -0
- package/docs/brand.md +151 -0
- package/docs/campaigns.md +96 -0
- package/docs/captcha.md +46 -0
- package/docs/certificates.md +68 -0
- package/docs/chat.md +47 -0
- package/docs/cloud.md +110 -0
- package/docs/company.md +64 -0
- package/docs/directory.md +140 -0
- package/docs/disperse.md +46 -0
- package/docs/domain.md +56 -0
- package/docs/edge.md +234 -0
- package/docs/email.md +42 -0
- package/docs/forms.md +48 -0
- package/docs/index.md +109 -0
- package/docs/migration.md +203 -0
- package/docs/migrations.md +56 -0
- package/docs/monitoring.md +41 -0
- package/docs/newsletter.md +48 -0
- package/docs/payment.md +83 -0
- package/docs/repo.md +48 -0
- package/docs/search.md +44 -0
- package/docs/seo.md +34 -0
- package/docs/server.md +37 -0
- package/docs/shared/agent-docs.md +89 -0
- package/docs/shared/analytics.md +612 -0
- package/docs/shared/brands.md +51 -0
- package/docs/shared/breaking-changes.md +497 -0
- package/docs/shared/config.md +1387 -0
- package/docs/shared/deploys.md +215 -0
- package/docs/shared/icons.md +201 -0
- package/docs/shared/local-dev.md +147 -0
- package/docs/shared/logging.md +202 -0
- package/docs/shared/monitoring.md +153 -0
- package/docs/shared/publishing.md +183 -0
- package/docs/shared/rulings.md +34 -0
- package/docs/shared/testing.md +147 -0
- package/docs/shared/theming.md +604 -0
- package/docs/shared/translation.md +291 -0
- package/docs/shared/updates.md +61 -0
- package/docs/testing.md +34 -0
- package/docs/update.md +36 -0
- package/docs/workspace.md +64 -0
- package/package.json +88 -0
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `omega update` core — the ONE dependency-freshness implementation behind
|
|
3
|
+
* every framework's update verb (npu-outdated semantics, Ian's node-power-user
|
|
4
|
+
* tool): per dependency report installed vs wanted vs latest, classify the
|
|
5
|
+
* jump (patch/minor/major), and QUARANTINE releases younger than --min-age
|
|
6
|
+
* days (default 7 — a brand-new publish may be a compromised one; hold it
|
|
7
|
+
* back unless explicitly forced). Report-only by default; --apply installs
|
|
8
|
+
* the non-quarantined, non-breaking set (majors need the explicit --major
|
|
9
|
+
* opt-in) through `npu install` when npu is on the machine, else plain
|
|
10
|
+
* `npm install` with a loud note.
|
|
11
|
+
*
|
|
12
|
+
* `file:`/`link:`/git specs have no registry story (the local era's linked
|
|
13
|
+
* @omega.js packages) — they're skipped with a dim note, never updated.
|
|
14
|
+
*
|
|
15
|
+
* Everything effectful is injectable (registry lookup, clock, exec) so the
|
|
16
|
+
* test suite never touches the network; the live path is the thin defaults.
|
|
17
|
+
*/
|
|
18
|
+
const path = require('node:path');
|
|
19
|
+
const fs = require('node:fs');
|
|
20
|
+
const { execSync } = require('node:child_process');
|
|
21
|
+
|
|
22
|
+
const REGISTRY_BASE = 'https://registry.npmjs.org';
|
|
23
|
+
const DEFAULT_MIN_AGE_DAYS = 7;
|
|
24
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
25
|
+
|
|
26
|
+
// Specs with no registry story: local links, git, tarball URLs, and the
|
|
27
|
+
// bare `owner/repo` GitHub shorthand (never a valid version range).
|
|
28
|
+
const NON_REGISTRY_SPEC = /^(file:|link:|workspace:|portal:|git\+|git:|github:|https?:)/;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Parse a semver-ish version string into comparable parts.
|
|
32
|
+
* @param {string} value - e.g. '1.2.3', 'v1.2.3', '1.2.3-beta.1'
|
|
33
|
+
* @returns {{ major: number, minor: number, patch: number, prerelease: string|null }|null}
|
|
34
|
+
*/
|
|
35
|
+
function parseVersion(value) {
|
|
36
|
+
const match = String(value || '').trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/);
|
|
37
|
+
if (!match) return null;
|
|
38
|
+
return {
|
|
39
|
+
major: Number(match[1]),
|
|
40
|
+
minor: Number(match[2]),
|
|
41
|
+
patch: Number(match[3]),
|
|
42
|
+
prerelease: match[4] || null,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Compare two version strings (-1 / 0 / 1). A prerelease sorts BEFORE its
|
|
48
|
+
* release triple; two prereleases compare lexically (enough for max-picking).
|
|
49
|
+
* @param {string} a
|
|
50
|
+
* @param {string} b
|
|
51
|
+
* @returns {number}
|
|
52
|
+
*/
|
|
53
|
+
function compareVersions(a, b) {
|
|
54
|
+
const va = parseVersion(a);
|
|
55
|
+
const vb = parseVersion(b);
|
|
56
|
+
if (!va || !vb) return 0;
|
|
57
|
+
|
|
58
|
+
for (const key of ['major', 'minor', 'patch']) {
|
|
59
|
+
if (va[key] !== vb[key]) return va[key] < vb[key] ? -1 : 1;
|
|
60
|
+
}
|
|
61
|
+
if (va.prerelease === vb.prerelease) return 0;
|
|
62
|
+
if (va.prerelease === null) return 1;
|
|
63
|
+
if (vb.prerelease === null) return -1;
|
|
64
|
+
return va.prerelease < vb.prerelease ? -1 : 1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Classify the jump between two versions.
|
|
69
|
+
* @param {string} from
|
|
70
|
+
* @param {string} to
|
|
71
|
+
* @returns {'major'|'minor'|'patch'|null} null when equal/unparseable
|
|
72
|
+
*/
|
|
73
|
+
function classifyBump(from, to) {
|
|
74
|
+
const a = parseVersion(from);
|
|
75
|
+
const b = parseVersion(to);
|
|
76
|
+
if (!a || !b || compareVersions(from, to) === 0) return null;
|
|
77
|
+
if (a.major !== b.major) return 'major';
|
|
78
|
+
if (a.minor !== b.minor) return 'minor';
|
|
79
|
+
return 'patch';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Whether a spec points at the npm registry at all.
|
|
84
|
+
* @param {string} spec - package.json dependency value
|
|
85
|
+
* @returns {boolean}
|
|
86
|
+
*/
|
|
87
|
+
function isRegistrySpec(spec) {
|
|
88
|
+
const value = String(spec || '').trim();
|
|
89
|
+
if (NON_REGISTRY_SPEC.test(value)) return false;
|
|
90
|
+
// GitHub shorthand: owner/repo — a '/' never appears in a version range
|
|
91
|
+
if (value.includes('/')) return false;
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Extract the base version from a range spec ('^1.2.3' → '1.2.3').
|
|
97
|
+
* @param {string} spec
|
|
98
|
+
* @returns {string|null} null when the spec has no single base ('*', 'latest', '1.x')
|
|
99
|
+
*/
|
|
100
|
+
function cleanSpec(spec) {
|
|
101
|
+
const match = String(spec || '').trim().match(/^[\^~=]?\s*v?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/);
|
|
102
|
+
return match ? match[1] : null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Highest stable version satisfying a range spec — npm-outdated's "wanted".
|
|
107
|
+
* Understands the specs this ecosystem actually writes: exact, ^, ~, >=,
|
|
108
|
+
* x-ranges ('1.2.x', '1.x', '1'), and '*'/''/'latest'. Anything else → null.
|
|
109
|
+
* @param {string} spec
|
|
110
|
+
* @param {string[]} versions - all published versions
|
|
111
|
+
* @returns {string|null}
|
|
112
|
+
*/
|
|
113
|
+
function resolveWanted(spec, versions) {
|
|
114
|
+
const value = String(spec || '').trim();
|
|
115
|
+
const stable = versions.filter((v) => {
|
|
116
|
+
const parsed = parseVersion(v);
|
|
117
|
+
return parsed && !parsed.prerelease;
|
|
118
|
+
}).sort(compareVersions);
|
|
119
|
+
const max = (list) => (list.length > 0 ? list[list.length - 1] : null);
|
|
120
|
+
|
|
121
|
+
if (value === '*' || value === '' || value === 'latest') return max(stable);
|
|
122
|
+
|
|
123
|
+
const base = cleanSpec(value);
|
|
124
|
+
if (base) {
|
|
125
|
+
const parsed = parseVersion(base);
|
|
126
|
+
const atLeast = stable.filter((v) => compareVersions(v, base) >= 0);
|
|
127
|
+
|
|
128
|
+
if (value.startsWith('^')) {
|
|
129
|
+
// Caret: same major; 0.x pins the minor; 0.0.x pins the patch
|
|
130
|
+
return max(atLeast.filter((v) => {
|
|
131
|
+
const candidate = parseVersion(v);
|
|
132
|
+
if (candidate.major !== parsed.major) return false;
|
|
133
|
+
if (parsed.major === 0 && candidate.minor !== parsed.minor) return false;
|
|
134
|
+
if (parsed.major === 0 && parsed.minor === 0 && candidate.patch !== parsed.patch) return false;
|
|
135
|
+
return true;
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
if (value.startsWith('~')) {
|
|
139
|
+
return max(atLeast.filter((v) => {
|
|
140
|
+
const candidate = parseVersion(v);
|
|
141
|
+
return candidate.major === parsed.major && candidate.minor === parsed.minor;
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
// Exact (with or without '=')
|
|
145
|
+
return versions.includes(base) ? base : null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (value.startsWith('>=')) {
|
|
149
|
+
const floor = cleanSpec(value.slice(2));
|
|
150
|
+
return floor ? max(stable.filter((v) => compareVersions(v, floor) >= 0)) : null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// x-ranges: '1', '1.x', '1.2', '1.2.x', '1.2.*' ('1.2.3' exact was handled above)
|
|
154
|
+
const xMatch = value.match(/^(\d+)(?:\.(\d+|[x*]))?(?:\.[x*])?$/);
|
|
155
|
+
if (xMatch) {
|
|
156
|
+
const major = Number(xMatch[1]);
|
|
157
|
+
const minor = xMatch[2] === undefined || /[x*]/.test(xMatch[2]) ? null : Number(xMatch[2]);
|
|
158
|
+
return max(stable.filter((v) => {
|
|
159
|
+
const candidate = parseVersion(v);
|
|
160
|
+
return candidate.major === major && (minor === null || candidate.minor === minor);
|
|
161
|
+
}));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Highest stable version within a bump tier of `current` — npu's
|
|
169
|
+
* patch/minor/latest targets.
|
|
170
|
+
* @param {string[]} versions
|
|
171
|
+
* @param {string} current
|
|
172
|
+
* @param {'patch'|'minor'|'latest'} tier
|
|
173
|
+
* @returns {string|null}
|
|
174
|
+
*/
|
|
175
|
+
function highestWithin(versions, current, tier) {
|
|
176
|
+
const parsed = parseVersion(current);
|
|
177
|
+
if (!parsed) return null;
|
|
178
|
+
|
|
179
|
+
const candidates = versions.filter((v) => {
|
|
180
|
+
const candidate = parseVersion(v);
|
|
181
|
+
if (!candidate || candidate.prerelease) return false;
|
|
182
|
+
if (compareVersions(v, current) < 0) return false;
|
|
183
|
+
if (tier === 'patch') return candidate.major === parsed.major && candidate.minor === parsed.minor;
|
|
184
|
+
if (tier === 'minor') return candidate.major === parsed.major;
|
|
185
|
+
return true;
|
|
186
|
+
}).sort(compareVersions);
|
|
187
|
+
|
|
188
|
+
return candidates.length > 0 ? candidates[candidates.length - 1] : null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Find a dependency's physically installed version — nearest node_modules
|
|
193
|
+
* copy walking UP from dir (npm hoists to the brand root in brand monorepos).
|
|
194
|
+
* @param {string} name - package name
|
|
195
|
+
* @param {string} dir - starting directory
|
|
196
|
+
* @returns {string|null}
|
|
197
|
+
*/
|
|
198
|
+
function findInstalledVersion(name, dir) {
|
|
199
|
+
let current = path.resolve(dir);
|
|
200
|
+
for (;;) {
|
|
201
|
+
const manifest = path.join(current, 'node_modules', ...name.split('/'), 'package.json');
|
|
202
|
+
if (fs.existsSync(manifest)) {
|
|
203
|
+
try {
|
|
204
|
+
return JSON.parse(fs.readFileSync(manifest, 'utf8')).version || null;
|
|
205
|
+
} catch (e) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
const parent = path.dirname(current);
|
|
210
|
+
if (parent === current) return null;
|
|
211
|
+
current = parent;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Live registry lookup — full packument (the abbreviated form has no publish
|
|
217
|
+
* times). The ONE network call of the verb; tests inject a fixture instead.
|
|
218
|
+
* @param {string} name - package name
|
|
219
|
+
* @param {object} [options]
|
|
220
|
+
* @param {function} [options.fetchFn] - injectable fetch
|
|
221
|
+
* @param {string} [options.registryUrl] - registry base URL
|
|
222
|
+
* @returns {Promise<{ latest: string|null, versions: string[], time: object }>}
|
|
223
|
+
*/
|
|
224
|
+
async function fetchPackument(name, options = {}) {
|
|
225
|
+
const fetchFn = options.fetchFn || fetch;
|
|
226
|
+
const base = options.registryUrl || REGISTRY_BASE;
|
|
227
|
+
const response = await fetchFn(`${base}/${name.replace('/', '%2F')}`, {
|
|
228
|
+
headers: { Accept: 'application/json' },
|
|
229
|
+
});
|
|
230
|
+
if (response.status === 404) {
|
|
231
|
+
// Zero publishes so far for @omega.js/* (HARD RULE 2) — a 404 is expected
|
|
232
|
+
// in-monorepo, so say what it means instead of a bare status code
|
|
233
|
+
throw new Error('not on the registry (unpublished?)');
|
|
234
|
+
}
|
|
235
|
+
if (!response.ok) {
|
|
236
|
+
throw new Error(`registry ${response.status} for ${name}`);
|
|
237
|
+
}
|
|
238
|
+
const body = await response.json();
|
|
239
|
+
return {
|
|
240
|
+
latest: body['dist-tags']?.latest || null,
|
|
241
|
+
versions: Object.keys(body.versions || {}),
|
|
242
|
+
time: body.time || {},
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Build the full update report for one target's package.json.
|
|
248
|
+
* @param {object} options
|
|
249
|
+
* @param {string} options.dir - target directory (package.json + node_modules climb)
|
|
250
|
+
* @param {function} options.lookup - async (name) => { latest, versions, time }
|
|
251
|
+
* @param {number} [options.now] - clock (ms epoch; injectable for tests)
|
|
252
|
+
* @param {number} [options.minAge] - quarantine threshold in days (default 7; 0 disables)
|
|
253
|
+
* @returns {Promise<{ project: string, rows: object[], locals: object[] }>}
|
|
254
|
+
*/
|
|
255
|
+
async function buildUpdateReport(options) {
|
|
256
|
+
const dir = options.dir;
|
|
257
|
+
const now = options.now ?? Date.now();
|
|
258
|
+
const minAge = options.minAge ?? DEFAULT_MIN_AGE_DAYS;
|
|
259
|
+
|
|
260
|
+
const manifestPath = path.join(dir, 'package.json');
|
|
261
|
+
if (!fs.existsSync(manifestPath)) {
|
|
262
|
+
throw new Error(`No package.json in ${dir}`);
|
|
263
|
+
}
|
|
264
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
265
|
+
|
|
266
|
+
// `only` narrows the manifest to a named set — the brand-root shell rides
|
|
267
|
+
// the fan-out for its @omega.js/manager pin alone (#794), and a brand's own
|
|
268
|
+
// root tooling is none of that leg's business.
|
|
269
|
+
const only = Array.isArray(options.only) ? new Set(options.only) : null;
|
|
270
|
+
const pick = (deps) => (only ? Object.fromEntries(Object.entries(deps).filter(([name]) => only.has(name))) : deps);
|
|
271
|
+
|
|
272
|
+
const groups = [
|
|
273
|
+
['prod', pick(manifest.dependencies || {})],
|
|
274
|
+
['dev', pick(manifest.devDependencies || {})],
|
|
275
|
+
];
|
|
276
|
+
|
|
277
|
+
const locals = [];
|
|
278
|
+
const rows = [];
|
|
279
|
+
|
|
280
|
+
await Promise.all(groups.flatMap(([group, deps]) => Object.entries(deps).map(async ([name, spec]) => {
|
|
281
|
+
if (!isRegistrySpec(spec)) {
|
|
282
|
+
locals.push({ name, group, spec });
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const installed = findInstalledVersion(name, dir);
|
|
287
|
+
const current = cleanSpec(spec) || installed;
|
|
288
|
+
|
|
289
|
+
const row = {
|
|
290
|
+
name, group, spec, current, installed,
|
|
291
|
+
wanted: null, latest: null, patchTarget: null, minorTarget: null,
|
|
292
|
+
bump: null, publishedAt: null, ageDays: null, quarantined: false, error: null,
|
|
293
|
+
};
|
|
294
|
+
rows.push(row);
|
|
295
|
+
|
|
296
|
+
let packument;
|
|
297
|
+
try {
|
|
298
|
+
packument = await options.lookup(name);
|
|
299
|
+
} catch (e) {
|
|
300
|
+
row.error = e.message;
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
row.wanted = resolveWanted(spec, packument.versions);
|
|
305
|
+
row.latest = packument.latest
|
|
306
|
+
|| resolveWanted('*', packument.versions);
|
|
307
|
+
if (current) {
|
|
308
|
+
row.patchTarget = highestWithin(packument.versions, current, 'patch');
|
|
309
|
+
row.minorTarget = highestWithin(packument.versions, current, 'minor');
|
|
310
|
+
row.bump = row.latest ? classifyBump(current, row.latest) : null;
|
|
311
|
+
}
|
|
312
|
+
row.time = packument.time;
|
|
313
|
+
|
|
314
|
+
// Quarantine display: how fresh is the latest release?
|
|
315
|
+
const publishedAt = packument.time?.[row.latest];
|
|
316
|
+
if (publishedAt) {
|
|
317
|
+
row.publishedAt = new Date(publishedAt);
|
|
318
|
+
row.ageDays = Math.floor((now - row.publishedAt.getTime()) / DAY_MS);
|
|
319
|
+
row.quarantined = minAge > 0 && row.ageDays < minAge && installed !== row.latest;
|
|
320
|
+
}
|
|
321
|
+
})));
|
|
322
|
+
|
|
323
|
+
// Only rows needing attention, prod before dev, alpha within group
|
|
324
|
+
const rank = { prod: 0, dev: 1 };
|
|
325
|
+
const attention = rows
|
|
326
|
+
.filter((row) => row.error || (row.latest && row.latest !== row.current) || (row.installed && row.current && row.installed !== row.current))
|
|
327
|
+
.sort((a, b) => (rank[a.group] - rank[b.group]) || a.name.localeCompare(b.name));
|
|
328
|
+
|
|
329
|
+
locals.sort((a, b) => a.name.localeCompare(b.name));
|
|
330
|
+
|
|
331
|
+
return { project: manifest.name || path.basename(dir), rows: attention, locals };
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Pick the updates an --apply run installs. Default tier is MINOR (highest
|
|
336
|
+
* same-major — npu's non-breaking lane); --major unlocks latest. A target
|
|
337
|
+
* published < minAge days ago is HELD (quarantine) unless already installed.
|
|
338
|
+
* @param {object[]} rows - buildUpdateReport rows
|
|
339
|
+
* @param {object} [options]
|
|
340
|
+
* @param {boolean} [options.major] - allow breaking jumps to latest
|
|
341
|
+
* @param {number} [options.minAge] - quarantine threshold in days (0 disables)
|
|
342
|
+
* @param {number} [options.now] - clock (injectable)
|
|
343
|
+
* @returns {{ updates: object[], quarantined: object[], majorsHeld: object[] }}
|
|
344
|
+
*/
|
|
345
|
+
function selectUpdates(rows, options = {}) {
|
|
346
|
+
const minAge = options.minAge ?? DEFAULT_MIN_AGE_DAYS;
|
|
347
|
+
const now = options.now ?? Date.now();
|
|
348
|
+
|
|
349
|
+
const updates = [];
|
|
350
|
+
const quarantined = [];
|
|
351
|
+
const majorsHeld = [];
|
|
352
|
+
|
|
353
|
+
for (const row of rows) {
|
|
354
|
+
if (row.error || !row.current) continue;
|
|
355
|
+
|
|
356
|
+
const target = options.major ? row.latest : row.minorTarget;
|
|
357
|
+
if (!target || compareVersions(target, row.current) <= 0) {
|
|
358
|
+
// Nothing applicable in-tier; note the held major so the human sees it
|
|
359
|
+
if (!options.major && row.bump === 'major' && row.latest !== row.installed) {
|
|
360
|
+
majorsHeld.push({ name: row.name, from: row.current, to: row.latest });
|
|
361
|
+
}
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Quarantine the TARGET version by its own publish age
|
|
366
|
+
const publishedAt = row.time?.[target];
|
|
367
|
+
const ageDays = publishedAt ? Math.floor((now - new Date(publishedAt).getTime()) / DAY_MS) : null;
|
|
368
|
+
if (minAge > 0 && ageDays !== null && ageDays < minAge && row.installed !== target) {
|
|
369
|
+
quarantined.push({ name: row.name, from: row.current, to: target, ageDays });
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
updates.push({ name: row.name, group: row.group, from: row.current, to: target, bump: classifyBump(row.current, target) });
|
|
374
|
+
|
|
375
|
+
if (!options.major && row.bump === 'major' && row.latest !== target) {
|
|
376
|
+
majorsHeld.push({ name: row.name, from: row.current, to: row.latest });
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return { updates, quarantined, majorsHeld };
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Whether npu (node-power-user — the Socket-firewalled npm wrapper) exists
|
|
385
|
+
* on this machine.
|
|
386
|
+
* @param {object} [options]
|
|
387
|
+
* @param {function} [options.execFn] - injectable exec
|
|
388
|
+
* @returns {boolean}
|
|
389
|
+
*/
|
|
390
|
+
function detectNpu(options = {}) {
|
|
391
|
+
const execFn = options.execFn || ((cmd, opts) => execSync(cmd, opts));
|
|
392
|
+
try {
|
|
393
|
+
execFn('npu --version', { stdio: ['ignore', 'pipe', 'ignore'] });
|
|
394
|
+
return true;
|
|
395
|
+
} catch (e) {
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Build the install command(s) for a selected update set — one per dep group
|
|
402
|
+
* (npm relocates a dev dep to dependencies without --save-dev). Installs run
|
|
403
|
+
* through `npu install` when npu exists (Socket supply-chain firewall);
|
|
404
|
+
* otherwise plain `npm install` (the caller warns loudly).
|
|
405
|
+
*
|
|
406
|
+
* The @omega.js family rides its own command per group, with `--save-exact`
|
|
407
|
+
* (#794): npm's default save-prefix writes `^<version>`, so the one verb that
|
|
408
|
+
* is ALLOWED to move a brand would have un-pinned the family it just moved.
|
|
409
|
+
* Everything else keeps npm's default prefix — the pin is the family's rule,
|
|
410
|
+
* not a rule for the whole dependency tree.
|
|
411
|
+
* @param {object[]} updates - selectUpdates().updates
|
|
412
|
+
* @param {object} options
|
|
413
|
+
* @param {boolean} options.hasNpu
|
|
414
|
+
* @returns {Array<{ command: string, group: string }>}
|
|
415
|
+
*/
|
|
416
|
+
function buildInstallCommands(updates, options) {
|
|
417
|
+
const bin = options.hasNpu ? 'npu install' : 'npm install';
|
|
418
|
+
const commands = [];
|
|
419
|
+
|
|
420
|
+
for (const [group, flag] of [['prod', ''], ['dev', ' --save-dev']]) {
|
|
421
|
+
const inGroup = updates.filter((update) => update.group === group);
|
|
422
|
+
|
|
423
|
+
for (const [family, extra] of [[false, ''], [true, ' --save-exact']]) {
|
|
424
|
+
const specs = inGroup
|
|
425
|
+
.filter((update) => update.name.startsWith('@omega.js/') === family)
|
|
426
|
+
.map((update) => `${update.name}@${update.to}`);
|
|
427
|
+
if (specs.length > 0) {
|
|
428
|
+
commands.push({ command: `${bin} ${specs.join(' ')}${flag}${extra}`, group });
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return commands;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Render the report as plain padded-table lines (framework wrappers print
|
|
438
|
+
* them through their own logger; no color dependency in the shared core).
|
|
439
|
+
* @param {{ project: string, rows: object[], locals: object[] }} report
|
|
440
|
+
* @param {object} [options]
|
|
441
|
+
* @param {number} [options.minAge]
|
|
442
|
+
* @returns {string[]}
|
|
443
|
+
*/
|
|
444
|
+
function formatReport(report, options = {}) {
|
|
445
|
+
const minAge = options.minAge ?? DEFAULT_MIN_AGE_DAYS;
|
|
446
|
+
const lines = [];
|
|
447
|
+
|
|
448
|
+
if (report.rows.length === 0) {
|
|
449
|
+
lines.push('All registry dependencies are up to date.');
|
|
450
|
+
} else {
|
|
451
|
+
const header = ['Package', 'Group', 'Current', 'Installed', 'Wanted', 'Latest', 'Bump', 'Released', 'Status'];
|
|
452
|
+
const body = report.rows.map((row) => {
|
|
453
|
+
const released = row.publishedAt
|
|
454
|
+
? `${row.publishedAt.toISOString().split('T')[0]} (${row.ageDays}d)`
|
|
455
|
+
: '-';
|
|
456
|
+
const status = row.error ? `lookup failed: ${row.error}`
|
|
457
|
+
: row.quarantined ? 'QUARANTINED'
|
|
458
|
+
: '';
|
|
459
|
+
return [
|
|
460
|
+
row.name, row.group, row.current || '-', row.installed || '-',
|
|
461
|
+
row.wanted || '-', row.latest || '-', row.bump || '-', released, status,
|
|
462
|
+
];
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
const widths = header.map((cell, index) => Math.max(cell.length, ...body.map((cells) => String(cells[index]).length)));
|
|
466
|
+
const render = (cells) => cells.map((cell, index) => String(cell).padEnd(widths[index])).join(' ').trimEnd();
|
|
467
|
+
lines.push(render(header));
|
|
468
|
+
lines.push(widths.map((width) => '-'.repeat(width)).join(' '));
|
|
469
|
+
for (const cells of body) lines.push(render(cells));
|
|
470
|
+
|
|
471
|
+
if (report.rows.some((row) => row.quarantined)) {
|
|
472
|
+
lines.push(`QUARANTINED = latest published < ${minAge} days ago (held from --apply; --min-age 0 or --force-fresh overrides)`);
|
|
473
|
+
}
|
|
474
|
+
if (report.rows.some((row) => row.bump === 'major')) {
|
|
475
|
+
lines.push('major = breaking — never auto-applied (explicit --major opt-in)');
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
for (const local of report.locals) {
|
|
480
|
+
lines.push(`skipped ${local.name} (${local.spec}) — local spec, no registry story`);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return lines;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* The one verb body every wrapper calls: report (default), then apply when
|
|
488
|
+
* asked. Flags mirror npu: --apply, --major, --min-age N (default 7, 0
|
|
489
|
+
* disables), --force-fresh (alias for --min-age 0).
|
|
490
|
+
* @param {object} [options]
|
|
491
|
+
* @param {string} [options.dir] - target directory (default cwd)
|
|
492
|
+
* @param {string[]} [options.only] - check just these package names (#794)
|
|
493
|
+
* @param {boolean} [options.apply] - install the selected set
|
|
494
|
+
* @param {boolean} [options.major] - allow breaking jumps (with --apply)
|
|
495
|
+
* @param {number} [options.minAge] - quarantine threshold in days
|
|
496
|
+
* @param {boolean} [options.forceFresh] - disable the age quarantine
|
|
497
|
+
* @param {object} [options.logger] - logger with log/warn (default console)
|
|
498
|
+
* @param {function} [options.lookup] - injectable registry lookup (tests)
|
|
499
|
+
* @param {function} [options.execFn] - injectable exec (tests)
|
|
500
|
+
* @param {number} [options.now] - injectable clock (tests)
|
|
501
|
+
* @param {boolean} [options.hasNpu] - injectable npu detection (tests)
|
|
502
|
+
* @returns {Promise<{ report: object, selection: object|null, commands: object[] }>}
|
|
503
|
+
*/
|
|
504
|
+
async function runUpdate(options = {}) {
|
|
505
|
+
const dir = options.dir || process.cwd();
|
|
506
|
+
const logger = options.logger || console;
|
|
507
|
+
const now = options.now ?? Date.now();
|
|
508
|
+
const minAge = options.forceFresh ? 0 : Number(options.minAge ?? DEFAULT_MIN_AGE_DAYS);
|
|
509
|
+
const lookup = options.lookup || ((name) => fetchPackument(name));
|
|
510
|
+
|
|
511
|
+
const report = await buildUpdateReport({ dir, lookup, now, minAge, only: options.only });
|
|
512
|
+
logger.log(`Dependency report for ${report.project}:`);
|
|
513
|
+
for (const line of formatReport(report, { minAge })) logger.log(line);
|
|
514
|
+
|
|
515
|
+
if (!options.apply) {
|
|
516
|
+
if (report.rows.length > 0) {
|
|
517
|
+
logger.log('Report only — `omega update --apply` installs the non-quarantined, non-breaking set (--major for breaking).');
|
|
518
|
+
}
|
|
519
|
+
return { report, selection: null, commands: [] };
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const selection = selectUpdates(report.rows, { major: options.major, minAge, now });
|
|
523
|
+
|
|
524
|
+
for (const held of selection.quarantined) {
|
|
525
|
+
logger.warn(`held ${held.name}@${held.to} — published ${held.ageDays}d ago (< ${minAge}d quarantine)`);
|
|
526
|
+
}
|
|
527
|
+
for (const held of selection.majorsHeld) {
|
|
528
|
+
logger.warn(`held ${held.name} ${held.from} → ${held.to} — major (breaking); re-run with --major to include`);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (selection.updates.length === 0) {
|
|
532
|
+
logger.log('Nothing to apply.');
|
|
533
|
+
return { report, selection, commands: [] };
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const hasNpu = options.hasNpu ?? detectNpu({ execFn: options.execFn });
|
|
537
|
+
if (!hasNpu) {
|
|
538
|
+
logger.warn('npu (node-power-user) is NOT on this machine — installing via plain `npm install`, WITHOUT the Socket supply-chain firewall.');
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const commands = buildInstallCommands(selection.updates, { hasNpu });
|
|
542
|
+
const execFn = options.execFn || ((cmd, opts) => execSync(cmd, opts));
|
|
543
|
+
for (const entry of commands) {
|
|
544
|
+
logger.log(`Running: ${entry.command}`);
|
|
545
|
+
execFn(entry.command, { cwd: dir, stdio: 'inherit' });
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
logger.log(`Applied ${selection.updates.length} update(s).`);
|
|
549
|
+
return { report, selection, commands };
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
module.exports = {
|
|
553
|
+
DEFAULT_MIN_AGE_DAYS,
|
|
554
|
+
parseVersion,
|
|
555
|
+
compareVersions,
|
|
556
|
+
classifyBump,
|
|
557
|
+
isRegistrySpec,
|
|
558
|
+
cleanSpec,
|
|
559
|
+
resolveWanted,
|
|
560
|
+
highestWithin,
|
|
561
|
+
findInstalledVersion,
|
|
562
|
+
fetchPackument,
|
|
563
|
+
buildUpdateReport,
|
|
564
|
+
selectUpdates,
|
|
565
|
+
detectNpu,
|
|
566
|
+
buildInstallCommands,
|
|
567
|
+
formatReport,
|
|
568
|
+
runUpdate,
|
|
569
|
+
};
|