@ultimat3/cli 29.4.0 → 30.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.md +64 -40
- package/README.md +61 -11
- package/package.json +42 -28
- package/src/affected-tests.ts +97 -0
- package/src/affected-unowned.ts +43 -0
- package/src/affected.ts +25 -6
- package/src/app-boundaries.ts +29 -1
- package/src/app-evals.ts +12 -4
- package/src/app-load-unresolved.ts +62 -0
- package/src/app-load.ts +30 -37
- package/src/app-mcp.ts +14 -2
- package/src/app-module-graph.ts +232 -0
- package/src/app-reload-graph.ts +2 -2
- package/src/app-root.ts +37 -1
- package/src/bin.ts +1 -0
- package/src/boundary-findings.ts +6 -0
- package/src/budgets-freshness.ts +32 -0
- package/src/budgets.ts +20 -1
- package/src/build-capture-env.ts +9 -0
- package/src/cdp-shot-a11y.ts +2 -39
- package/src/ci-tested.ts +243 -0
- package/src/client-emit-customer-runtime.ts +139 -0
- package/src/client-emit-customer.ts +108 -0
- package/src/client-emit-drift.ts +72 -0
- package/src/client-emit-header.ts +67 -0
- package/src/client-emit-schemas.ts +175 -0
- package/src/client-emit-types.ts +121 -0
- package/src/client-emit-vendor.ts +146 -0
- package/src/client-errors-routes.ts +229 -0
- package/src/client-errors-script.ts +74 -0
- package/src/client-vitals-route.ts +197 -0
- package/src/client-vitals-script.ts +56 -0
- package/src/cmd-affected.ts +23 -0
- package/src/cmd-build-spec.ts +7 -1
- package/src/cmd-build.ts +35 -9
- package/src/cmd-ci-spec.ts +42 -6
- package/src/cmd-ci.ts +99 -2
- package/src/cmd-client-customer.ts +134 -0
- package/src/cmd-client-source.ts +81 -0
- package/src/cmd-client-spec.ts +48 -0
- package/src/cmd-client-write.ts +41 -0
- package/src/cmd-client.ts +111 -0
- package/src/cmd-db-recount.ts +120 -0
- package/src/cmd-db-spec.ts +36 -6
- package/src/cmd-db.ts +11 -3
- package/src/cmd-deploy-compose.ts +61 -3
- package/src/cmd-deploy-drain.ts +10 -2
- package/src/cmd-deploy-helm.ts +11 -0
- package/src/cmd-deploy-preflight.ts +118 -0
- package/src/cmd-deploy-spec.ts +58 -6
- package/src/cmd-deploy-verify.ts +380 -0
- package/src/cmd-deploy.ts +42 -9
- package/src/cmd-docs.ts +34 -4
- package/src/cmd-doctor.ts +66 -9
- package/src/cmd-drill-spec.ts +25 -0
- package/src/cmd-drill.ts +176 -0
- package/src/cmd-env-init.ts +102 -0
- package/src/cmd-env-spec.ts +10 -3
- package/src/cmd-env.ts +15 -10
- package/src/cmd-errors-recent.ts +60 -0
- package/src/cmd-errors-spec.ts +9 -1
- package/src/cmd-errors.ts +4 -0
- package/src/cmd-exercise-spec.ts +19 -0
- package/src/cmd-exercise.ts +129 -0
- package/src/cmd-generate-spec.ts +9 -1
- package/src/cmd-generate.ts +32 -27
- package/src/cmd-graph-load.ts +97 -0
- package/src/cmd-graph-spec.ts +16 -0
- package/src/cmd-graph.ts +73 -0
- package/src/cmd-i18n.ts +4 -1
- package/src/cmd-impact-spec.ts +13 -0
- package/src/cmd-impact.ts +64 -0
- package/src/cmd-jobs-resolve.ts +76 -0
- package/src/cmd-jobs-spec.ts +21 -2
- package/src/cmd-jobs.ts +10 -6
- package/src/cmd-mail-spec.ts +28 -0
- package/src/cmd-mail.ts +70 -0
- package/src/cmd-mcp-lock.ts +69 -0
- package/src/cmd-mcp-spec.ts +23 -4
- package/src/cmd-mcp.ts +17 -3
- package/src/cmd-new-spec.ts +8 -1
- package/src/cmd-new.ts +61 -9
- package/src/cmd-planned.ts +0 -6
- package/src/cmd-repro-spec.ts +31 -0
- package/src/cmd-repro.ts +174 -0
- package/src/cmd-stop-spec.ts +21 -0
- package/src/cmd-stop.ts +166 -0
- package/src/cmd-test-spec.ts +17 -4
- package/src/cmd-test.ts +70 -14
- package/src/cmd-upgrade-spec.ts +26 -0
- package/src/cmd-upgrade.ts +274 -0
- package/src/cmd-verify-spec.ts +3 -3
- package/src/db-backfill.ts +6 -0
- package/src/db-diagnostics.ts +106 -0
- package/src/db-generate.ts +9 -0
- package/src/db-reset-template.ts +1 -1
- package/src/db-rolling.ts +45 -0
- package/src/db-seed-generate.ts +188 -0
- package/src/deploy-verify-findings.ts +98 -0
- package/src/deploy-verify-flags.ts +109 -0
- package/src/deploy-verify-grade.ts +185 -0
- package/src/deploy-verify-queues.ts +53 -0
- package/src/dev-boot.ts +35 -15
- package/src/dev-child-watch.ts +16 -4
- package/src/dev-dashboard-guard.ts +8 -1
- package/src/dev-dashboard.ts +7 -0
- package/src/dev-lock-identity.ts +61 -0
- package/src/dev-lock-port-holder.ts +80 -0
- package/src/dev-lock-steal.ts +124 -0
- package/src/dev-lock.ts +92 -120
- package/src/dev-mail-templates.ts +51 -0
- package/src/dev-rebuild.ts +26 -2
- package/src/dev-route-table.ts +17 -0
- package/src/dev-supervisor.ts +5 -2
- package/src/dev-watch-tree.ts +11 -10
- package/src/dev-watch.ts +1 -1
- package/src/dispatch.ts +29 -6
- package/src/docs-section.ts +69 -0
- package/src/docs-sources.ts +100 -0
- package/src/doctor-auth.ts +11 -2
- package/src/doctor-chart-alerts.ts +55 -0
- package/src/doctor-pooler.ts +107 -0
- package/src/doctor-rolling.ts +35 -0
- package/src/doctor-row-security.ts +40 -0
- package/src/document-options.ts +13 -0
- package/src/drill-sync-lane.ts +25 -0
- package/src/drill-targets.ts +39 -0
- package/src/duplicate-packages.ts +1 -1
- package/src/env-production.ts +93 -0
- package/src/error-codes-shot.ts +59 -0
- package/src/error-codes-tooling.ts +29 -0
- package/src/error-codes.ts +100 -54
- package/src/error-contract.ts +2 -2
- package/src/errors.ts +22 -3
- package/src/exercise-database.ts +99 -0
- package/src/exercise-findings.ts +159 -0
- package/src/exercise-overrides.ts +42 -0
- package/src/exercise-registration.ts +58 -0
- package/src/exercise-static.ts +88 -0
- package/src/exercise-subject.ts +69 -0
- package/src/failure-capture-boot.ts +30 -0
- package/src/framework-env.ts +7 -1
- package/src/framework-schema.ts +72 -5
- package/src/generate-catalog-locales.ts +19 -0
- package/src/generate-chart-alerts.ts +156 -0
- package/src/generate-feature-required.ts +65 -0
- package/src/generate-feature.ts +83 -6
- package/src/generate-files.ts +24 -0
- package/src/generate-format-written.ts +32 -0
- package/src/generate-kinds.ts +6 -0
- package/src/generate-sign-in-config.ts +100 -0
- package/src/generate-sign-in-manifest.ts +28 -0
- package/src/generate-sign-in-mode.ts +101 -0
- package/src/generate-sign-in.ts +250 -0
- package/src/generate-write.ts +44 -7
- package/src/hand-rolled-socket.ts +72 -0
- package/src/i18n-app-key-shapes.ts +29 -0
- package/src/i18n-audit.ts +10 -3
- package/src/i18n-registration.ts +18 -3
- package/src/index.ts +2 -0
- package/src/island-bundle.ts +4 -3
- package/src/island-disposer.ts +116 -0
- package/src/island-realtime-state.ts +34 -6
- package/src/island-realtime.ts +6 -2
- package/src/island-routes.ts +3 -2
- package/src/isr-query-advice.ts +6 -11
- package/src/isr-query-guard.ts +4 -3
- package/src/job-budget.ts +95 -0
- package/src/jobs-json.ts +11 -0
- package/src/lint-findings.ts +104 -0
- package/src/local-cli.ts +34 -4
- package/src/loopback-host.ts +2 -2
- package/src/mail-preview-findings.ts +24 -0
- package/src/mcp-errors.ts +82 -2
- package/src/mcp-host.ts +11 -1
- package/src/mcp-lock-findings.ts +57 -0
- package/src/measure-paths.ts +12 -4
- package/src/measure-scope.ts +27 -13
- package/src/messages-client.ts +23 -0
- package/src/messages-db.ts +56 -0
- package/src/messages-gate.ts +107 -0
- package/src/messages-repro.ts +11 -0
- package/src/messages-shot.ts +51 -0
- package/src/messages-upgrade.ts +45 -0
- package/src/messages.ts +53 -178
- package/src/metrics-endpoint.ts +23 -5
- package/src/new-shapes-generate.ts +71 -0
- package/src/new-shapes.ts +63 -0
- package/src/openapi-compile-ir.ts +68 -0
- package/src/openapi-compile-schema.ts +284 -0
- package/src/openapi-compile.ts +273 -0
- package/src/openapi-refuse.ts +45 -0
- package/src/otlp-export.ts +2 -1
- package/src/output-findings.ts +123 -0
- package/src/output.ts +32 -8
- package/src/package-boundaries.ts +110 -0
- package/src/page-pins.ts +23 -0
- package/src/plain-routes.ts +13 -0
- package/src/prerender-entries.ts +59 -0
- package/src/prerender-heaviest.ts +27 -0
- package/src/prerender-preload.ts +6 -0
- package/src/prerender-report.ts +12 -0
- package/src/prerender-seo-reads.ts +38 -0
- package/src/prerender-served.ts +22 -7
- package/src/prerender-vitals.ts +42 -0
- package/src/prerender.ts +61 -32
- package/src/pwa-artifacts.ts +4 -3
- package/src/pwa-manifest-locales.ts +1 -1
- package/src/registry.ts +18 -0
- package/src/repro-bundle.ts +65 -0
- package/src/repro-errors.ts +47 -0
- package/src/repro-job.ts +72 -0
- package/src/repro-literal.ts +41 -0
- package/src/repro-request.ts +48 -0
- package/src/repro-write.ts +100 -0
- package/src/retain-sweep.ts +27 -0
- package/src/retained-assets-boot.ts +113 -0
- package/src/retained-assets-layout.ts +110 -0
- package/src/retained-assets-read.ts +142 -0
- package/src/retained-assets-sweep.ts +70 -0
- package/src/retained-assets.ts +171 -0
- package/src/revalidated-response.ts +8 -5
- package/src/role-replicator.ts +7 -1
- package/src/role-start-devices.ts +119 -0
- package/src/role-start-types.ts +6 -0
- package/src/role-start.ts +22 -5
- package/src/role-sync.ts +7 -1
- package/src/route-islands.ts +42 -0
- package/src/route-table.ts +2 -1
- package/src/row-security-boot.ts +58 -0
- package/src/row-security-wanted.ts +32 -0
- package/src/runtime-admin.ts +0 -1
- package/src/runtime-assets-site-cache.ts +55 -0
- package/src/runtime-assets-site.ts +136 -0
- package/src/runtime-assets.ts +99 -55
- package/src/runtime-bindings.ts +19 -3
- package/src/runtime-cache.ts +23 -14
- package/src/runtime-isr-outcome.ts +114 -60
- package/src/runtime-purge.ts +14 -2
- package/src/runtime-queue.ts +31 -4
- package/src/runtime-render-document.ts +204 -0
- package/src/runtime-render.ts +48 -198
- package/src/runtime-replica-position.ts +62 -0
- package/src/runtime-replica.ts +18 -3
- package/src/runtime-s3-path-style.ts +17 -0
- package/src/runtime-services.ts +19 -6
- package/src/runtime-statement-bound.ts +22 -0
- package/src/runtime-storage-upload.ts +9 -0
- package/src/runtime-storage.ts +41 -81
- package/src/schema-diff-views.ts +54 -0
- package/src/schema-diff.ts +21 -3
- package/src/schema-drift.ts +28 -9
- package/src/schema-dump-drift.ts +91 -7
- package/src/script-csp.ts +1 -1
- package/src/seo-cards.ts +26 -0
- package/src/seo-routes.ts +22 -8
- package/src/serve-boot.ts +54 -21
- package/src/serve-drain.ts +1 -0
- package/src/serve-env.ts +26 -8
- package/src/serve-retire.ts +12 -6
- package/src/serve-web.ts +42 -4
- package/src/serve.ts +9 -7
- package/src/site-asset-routes.ts +7 -1
- package/src/site-config.ts +4 -0
- package/src/site-llms-pages.ts +106 -0
- package/src/site-llms.ts +64 -0
- package/src/site-seo-write.ts +4 -2
- package/src/site-seo.ts +55 -6
- package/src/source-mtime.ts +50 -0
- package/src/static-capture-errors.ts +41 -0
- package/src/static-capture.ts +56 -0
- package/src/static-cards-serve.ts +39 -0
- package/src/static-cards.ts +93 -0
- package/src/static-document.ts +14 -13
- package/src/static-images.ts +115 -0
- package/src/stream-hole-scope.ts +55 -0
- package/src/style-bundle.ts +1 -1
- package/src/style-routes.ts +3 -2
- package/src/sw-precache-plan.ts +2 -2
- package/src/sw-routes.ts +2 -2
- package/src/templates/action-tests.ts +306 -0
- package/src/templates/action.ts +81 -269
- package/src/templates/backfill.ts +1 -1
- package/src/templates/entity.ts +21 -5
- package/src/templates/github/ci.yml.ts +8 -9
- package/src/templates/guard-bare-error.ts +2 -2
- package/src/templates/guard-error-declared.ts +263 -0
- package/src/templates/guard-native-confirm.ts +132 -0
- package/src/templates/guard-raw-fetch.ts +151 -0
- package/src/templates/guard-raw-interval.ts +121 -0
- package/src/templates/guard-repo-raw-sql.ts +71 -2
- package/src/templates/job.ts +1 -1
- package/src/templates/naming.ts +5 -0
- package/src/templates/pinned/docker-compose.yml +6 -0
- package/src/templates/pinned-images.ts +8 -0
- package/src/templates/policy.ts +85 -2
- package/src/templates/query.ts +70 -13
- package/src/templates/resource-create.ts +3 -1
- package/src/templates/resource-form-island.ts +15 -4
- package/src/templates/resource-page-db.ts +83 -0
- package/src/templates/resource-page.ts +161 -52
- package/src/templates/route.ts +4 -6
- package/src/templates/scaffold-app.ts +3 -0
- package/src/templates/scaffold-auth.ts +2 -1
- package/src/templates/scaffold-bin.ts +5 -52
- package/src/templates/scaffold-claude-agents.ts +19 -19
- package/src/templates/scaffold-claude-commands.ts +28 -31
- package/src/templates/scaffold-claude.ts +26 -23
- package/src/templates/scaffold-container-compose.ts +6 -1
- package/src/templates/scaffold-container.ts +24 -4
- package/src/templates/scaffold-dashboard-bare.ts +3 -3
- package/src/templates/scaffold-dashboard-example-tests.ts +49 -9
- package/src/templates/scaffold-dashboard-example-view.ts +156 -0
- package/src/templates/scaffold-dashboard-example.ts +43 -131
- package/src/templates/scaffold-dashboard-shared.ts +3 -7
- package/src/templates/scaffold-db-client.ts +20 -6
- package/src/templates/scaffold-db-package.ts +5 -14
- package/src/templates/scaffold-docs.ts +73 -57
- package/src/templates/scaffold-domain-package.ts +18 -14
- package/src/templates/scaffold-entries.ts +7 -2
- package/src/templates/scaffold-exercise.ts +37 -0
- package/src/templates/scaffold-guards.ts +13 -1
- package/src/templates/scaffold-helm-alerts.ts +234 -0
- package/src/templates/scaffold-helm-templates.ts +7 -5
- package/src/templates/scaffold-helm.ts +46 -12
- package/src/templates/scaffold-i18n.ts +6 -2
- package/src/templates/scaffold-mcp-endpoint.ts +182 -0
- package/src/templates/scaffold-mcp-json.ts +31 -0
- package/src/templates/scaffold-mcp-package.ts +59 -56
- package/src/templates/scaffold-repo.ts +7 -2
- package/src/templates/scaffold-shell.ts +45 -79
- package/src/templates/scaffold-test-services.ts +13 -5
- package/src/templates/shapes/api-keys.ts +170 -0
- package/src/templates/shapes/api.ts +101 -0
- package/src/templates/shapes/blog-site.ts +340 -0
- package/src/templates/shapes/blog-slice.ts +265 -0
- package/src/templates/shapes/blog.ts +114 -0
- package/src/templates/shapes/dashboard-detail.ts +242 -0
- package/src/templates/shapes/dashboard.ts +53 -0
- package/src/templates/shapes/index.ts +17 -0
- package/src/templates/shapes/overlay.ts +99 -0
- package/src/templates/shapes/shared-edits.ts +46 -0
- package/src/templates/sign-in-action-tests.ts +237 -0
- package/src/templates/sign-in-authenticator.ts +112 -0
- package/src/templates/sign-in-oidc.ts +189 -0
- package/src/templates/sign-in-pages.ts +148 -0
- package/src/templates/sign-in-password.ts +216 -0
- package/src/templates/sign-in-shared.ts +268 -0
- package/src/templates/slice-foundation.ts +70 -15
- package/src/test-batches.ts +1 -1
- package/src/test-database.ts +1 -1
- package/src/test-shards.ts +7 -1
- package/src/test-workers.ts +18 -12
- package/src/tool-gate-names.ts +34 -0
- package/src/upgrade-agents-link.ts +49 -0
- package/src/upgrade-bun-floor.ts +225 -0
- package/src/upgrade-gate.ts +228 -0
- package/src/upgrade-pins.ts +63 -0
- package/src/upgrade-plan.ts +214 -0
- package/src/upgrade-registry.ts +53 -0
- package/src/upgrade-report.ts +139 -0
- package/src/upgrade-root.ts +74 -0
- package/src/verify-budgets-build.ts +118 -0
- package/src/verify-checks.ts +67 -24
- package/src/verify-coverage-run.ts +1 -1
- package/src/verify-e2e-lane.ts +41 -0
- package/src/verify-exercise.ts +64 -0
- package/src/verify-floor.ts +41 -0
- package/src/verify-merge.ts +19 -4
- package/src/verify-role-load.ts +2 -0
- package/src/verify-run.ts +28 -8
- package/src/verify-shard.ts +12 -2
- package/src/verify-skip-ceiling.ts +87 -0
- package/src/verify-step.ts +5 -0
- package/src/verify-tests.ts +25 -5
- package/src/view-drift.ts +55 -0
- package/src/worker-bundle.ts +3 -2
- package/src/workspace-graph.ts +38 -19
- package/src/write-generated.ts +43 -0
- package/src/zone-boot.ts +20 -0
- package/wiki/Actions.md +342 -0
- package/wiki/Admin-Dashboard.md +432 -0
- package/wiki/Agents.md +958 -0
- package/wiki/Auth.md +195 -0
- package/wiki/Bare-VM.md +111 -0
- package/wiki/Batching-And-Preloading.md +136 -0
- package/wiki/Building-Your-Own-Base.md +203 -0
- package/wiki/CI-Parallel-Gate.md +220 -0
- package/wiki/CLI-Reference.md +1894 -0
- package/wiki/Caching-And-Invalidation.md +255 -0
- package/wiki/Client-Data.md +194 -0
- package/wiki/Client-Navigation.md +513 -0
- package/wiki/Compared.md +93 -0
- package/wiki/Configuration.md +868 -0
- package/wiki/Contributing.md +12 -0
- package/wiki/Deploy-Recipes.md +122 -0
- package/wiki/Deployment.md +508 -0
- package/wiki/Entities-And-Migrations.md +1116 -0
- package/wiki/FAQ.md +175 -0
- package/wiki/Feature-Flags.md +65 -0
- package/wiki/Getting-Started.md +221 -0
- package/wiki/Home.md +160 -0
- package/wiki/I18n.md +239 -0
- package/wiki/Installation.md +206 -0
- package/wiki/Interface-Rules.md +339 -0
- package/wiki/Jobs-And-Workflows.md +783 -0
- package/wiki/Known-Gaps.md +185 -0
- package/wiki/MCP-And-AI.md +753 -0
- package/wiki/Mail.md +138 -0
- package/wiki/Migrating-An-Existing-App.md +594 -0
- package/wiki/Migrating-From-Django.md +54 -0
- package/wiki/Migrating-From-Laravel.md +53 -0
- package/wiki/Migrating-From-Node-Drizzle.md +53 -0
- package/wiki/Migrating-From-Rails.md +53 -0
- package/wiki/Migrations-And-Backfills.md +552 -0
- package/wiki/Money.md +338 -0
- package/wiki/N-Plus-One-Detection.md +197 -0
- package/wiki/Notify.md +208 -0
- package/wiki/Observability.md +245 -0
- package/wiki/Outbound-HTTP.md +109 -0
- package/wiki/PWA-And-Offline.md +343 -0
- package/wiki/Policies-And-Authz.md +251 -0
- package/wiki/Project-Layout.md +150 -0
- package/wiki/Queries-And-Live-Queries.md +429 -0
- package/wiki/Realtime.md +734 -0
- package/wiki/Resource-Management.md +121 -0
- package/wiki/Routes-And-Render-Modes.md +521 -0
- package/wiki/SEO.md +288 -0
- package/wiki/Scheduled-Tasks.md +175 -0
- package/wiki/Scraping.md +493 -0
- package/wiki/Shapes.md +63 -0
- package/wiki/Stability.md +97 -0
- package/wiki/Static-Assets.md +166 -0
- package/wiki/Storage-And-Uploads.md +404 -0
- package/wiki/Testing.md +1314 -0
- package/wiki/The-Eight-Primitives.md +247 -0
- package/wiki/Theming.md +362 -0
- package/wiki/Timezones-And-Dates.md +194 -0
- package/wiki/Troubleshooting.md +163 -0
- package/wiki/Tutorial-01-First-App.md +289 -0
- package/wiki/Tutorial-02-First-Feature.md +339 -0
- package/wiki/Tutorial-03-Auth-And-Admin.md +379 -0
- package/wiki/Tutorial-04-Jobs-And-Realtime.md +263 -0
- package/wiki/Tutorial-05-Deploy-Free.md +231 -0
- package/wiki/Tutorial-06-Growing-Up.md +158 -0
- package/wiki/UI-Components.md +156 -0
- package/wiki/Upgrading.md +3667 -0
- package/src/stored-object-headers.ts +0 -43
package/src/messages.ts
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
// built-in catalog in the same flat-key/loud-miss shape as @ultimat3/i18n instead of importing
|
|
4
4
|
// one. Missing keys render as ⟦key⟧, never as English fallback.
|
|
5
5
|
|
|
6
|
+
import { CLIENT_MESSAGES } from './messages-client';
|
|
7
|
+
import { DB_MESSAGES } from './messages-db';
|
|
8
|
+
import { GATE_MESSAGES } from './messages-gate';
|
|
9
|
+
import { REPRO_MESSAGES } from './messages-repro';
|
|
10
|
+
import { SHOT_MESSAGES } from './messages-shot';
|
|
11
|
+
import { UPGRADE_MESSAGES } from './messages-upgrade';
|
|
12
|
+
|
|
6
13
|
const CATALOG = {
|
|
7
14
|
'cli.tagline': 'Ultimate — one command means shippable',
|
|
8
15
|
'cli.usage': 'usage: x <command> [options]',
|
|
@@ -29,52 +36,6 @@ const CATALOG = {
|
|
|
29
36
|
'cli.cron.inMonths': 'in {months}',
|
|
30
37
|
'cli.cron.onDaysOfMonth': 'on day {days} of the month',
|
|
31
38
|
'cli.cron.onWeekdays': 'on {days}',
|
|
32
|
-
'cli.db.backfill.empty': 'no backfill has run against this database yet',
|
|
33
|
-
'cli.db.backfill.listed': '{count} backfill pass(es)',
|
|
34
|
-
/** The empty cell in a `x db backfill --list` column — a value, not a column key. */
|
|
35
|
-
'cli.db.backfill.none': '-',
|
|
36
|
-
'cli.db.backfill.pending': '{count} of {declared} declared backfill(s) never completed',
|
|
37
|
-
'cli.db.backfill.swept': 'every one of {declared} declared backfill(s) has completed',
|
|
38
|
-
// Every action counted, never derived: `count - enqueued` folded a deduped pass into "blocked",
|
|
39
|
-
// so the summary said blocked while `--json` said deduped for the same row — two renderers
|
|
40
|
-
// stating different facts about one run, which is the thing `--json` exists to make impossible.
|
|
41
|
-
'cli.db.backfill.planned':
|
|
42
|
-
'{count} backfill(s): {enqueued} enqueued, {deduped} already live, {blocked} blocked',
|
|
43
|
-
'cli.db.backfill.dryRun': '{count} backfill(s) would run — nothing written without --write',
|
|
44
|
-
'cli.db.branch.ready': 'branch {name} ready',
|
|
45
|
-
'cli.db.branch.dropped': 'branch {name} dropped',
|
|
46
|
-
'cli.db.branch.failed': 'branch command failed',
|
|
47
|
-
'cli.db.branch.listed': '{count} branch(es) of this database',
|
|
48
|
-
'cli.db.branch.none': 'this database has no branch',
|
|
49
|
-
/** The empty cell in an `x db branch list` column — a value, not a column key. */
|
|
50
|
-
'cli.db.branch.unknown': '-',
|
|
51
|
-
'cli.db.gen.failed': 'migration not generated',
|
|
52
|
-
'cli.db.gen.unchanged': 'entities and migrations agree — nothing to generate',
|
|
53
|
-
'cli.db.gen.blocked':
|
|
54
|
-
'nothing was compared — the app did not load ({count} finding(s)); fix them, then run x db gen again',
|
|
55
|
-
// A THIRD outcome, and it is neither of the other two: nothing to generate, but the sidecar the
|
|
56
|
-
// `drift` step reads did move — an edit under `packages/db/src` that implies no DDL. Rendering it
|
|
57
|
-
// as `written` would name a migration nobody can apply; as `unchanged`, it would hide a file this
|
|
58
|
-
// command wrote. `GeneratedFiles.outcome` is what `--json` carries the same distinction on.
|
|
59
|
-
'cli.db.gen.recorded': 'no migration needed — schema hash re-recorded in {file}',
|
|
60
|
-
// The fourth: no DDL and no sidecar moved, but `packages/db/schema/` did — a hand edit repaired,
|
|
61
|
-
// or a framework table that changed under an upgrade. `unchanged` would deny a write the lines
|
|
62
|
-
// below it list, and this command is `X_SCHEMA_DUMP_DRIFT`'s whole `fix:`.
|
|
63
|
-
'cli.db.gen.dumped': 'no migration needed — schema dump rewritten ({count} file(s))',
|
|
64
|
-
'cli.db.gen.written': 'migration {id} generated',
|
|
65
|
-
// Exit 1 and a migration on disk at once: the summary carries the half the exit code hides.
|
|
66
|
-
'cli.db.gen.writtenDumpFailed':
|
|
67
|
-
'migration {id} generated and written — the schema dump was not; do not generate it again',
|
|
68
|
-
'cli.db.migrate.applied': 'migrations applied',
|
|
69
|
-
'cli.db.migrate.failed': 'migration failed',
|
|
70
|
-
'cli.db.reset.done': 'database reset and migrated',
|
|
71
|
-
// Every seed counted per outcome, exactly as the backfill summary is: a replayed seed writes
|
|
72
|
-
// nothing and skips everything, and a total that hid that would make the second run look idle.
|
|
73
|
-
'cli.db.seed.done':
|
|
74
|
-
'{count} seed(s): {inserted} inserted, {updated} updated, {skipped} already stored',
|
|
75
|
-
'cli.db.seed.dryRun': '{count} seed(s) would run — nothing written while --dry-run is set',
|
|
76
|
-
'cli.db.seed.failed': '{failed} of {count} seed(s) failed',
|
|
77
|
-
'cli.db.seed.none': 'no seed matched — nothing to run',
|
|
78
39
|
'cli.dev.ready': 'dev ready on {url} — /_x mounted ({panels} panels), {services}',
|
|
79
40
|
// The mail and CDN halves of that boot line. Rendered text, so it lives here — while
|
|
80
41
|
// `describeMail`/`describeCdn` keep the same wording as the fixed vocabulary `x dev --json`
|
|
@@ -97,8 +58,24 @@ const CATALOG = {
|
|
|
97
58
|
'cli.dev.introspect': ' introspect {url}',
|
|
98
59
|
'cli.dev.manifest': ' manifest {path}',
|
|
99
60
|
'cli.dev.mcp': ' mcp POST {path}',
|
|
61
|
+
// `x stop` (`cmd-stop.ts`): the lock's holder, and only it.
|
|
62
|
+
'cli.stop.none': 'no x dev is running in this checkout',
|
|
63
|
+
'cli.stop.stale':
|
|
64
|
+
'the dev.lock names a pid another process now holds — nothing signalled; the next x dev clears the lock',
|
|
65
|
+
'cli.stop.stopped': 'stopped x dev (pid {pid}, port {port}) in {ms}ms',
|
|
66
|
+
'cli.stop.terminated':
|
|
67
|
+
'terminated x dev (pid {pid}, port {port}) in {ms}ms — Windows stops a process without a drain',
|
|
100
68
|
'cli.deploy.plan': 'containers only: {images} image, roles {roles}',
|
|
69
|
+
'cli.deploy.verified':
|
|
70
|
+
'build {build} held {windowMs} ms over {samples} sample(s); queues: {served} served, {idle} idle',
|
|
71
|
+
'cli.deploy.verifiedUngraded':
|
|
72
|
+
'build {build} held {windowMs} ms over {samples} sample(s); queues NOT graded: DATABASE_URL is unset, so no worker row was read',
|
|
73
|
+
'cli.deploy.unverified': 'build {build} not verified: {count} finding(s)',
|
|
101
74
|
'cli.doctor.clean': 'no findings — environment is shippable',
|
|
75
|
+
'cli.doctor.poolerBound':
|
|
76
|
+
'DATABASE_POOLER={pooler}, {role} role: statement_timeout {statement} ms, idle_in_transaction_session_timeout {idle} ms, as the session reads them',
|
|
77
|
+
'cli.doctor.poolerWakeOff':
|
|
78
|
+
'wake off, pickup within idlePollMaxMs ({ms} ms by default): a transaction pooler delivers no notification. For the wake, give this role a direct or session-pooled DATABASE_URL and unset DATABASE_POOLER',
|
|
102
79
|
'cli.doctor.findings': '{count} finding(s)',
|
|
103
80
|
'cli.doctor.guards':
|
|
104
81
|
'{count} shipped guard(s) this app does not hold — each line adds one, then x verify reports what it finds:',
|
|
@@ -107,6 +84,10 @@ const CATALOG = {
|
|
|
107
84
|
// `x doctor`'s other listing: what the coverage floor in x.verify.json does not see, and why.
|
|
108
85
|
'cli.doctor.coverageExclude':
|
|
109
86
|
'{count} path(s) x.verify.json excludes from the coverage floor — each with the reason it gives:',
|
|
87
|
+
'cli.doctor.chartAlerts': '{count} monitoring template(s) missing from the chart:',
|
|
88
|
+
// `x doctor`'s reminder: the rolling rail is off until this key is set (`doctor-rolling.ts`).
|
|
89
|
+
'cli.doctor.rollingSince':
|
|
90
|
+
'the rolling-deploy rail is off: set "rollingSince": "{newest}" in x.verify.json to judge every migration after your newest',
|
|
110
91
|
'cli.docs.code': '{code} is an error code — x errors explain answers it',
|
|
111
92
|
'cli.docs.exports': 'exports: {list}',
|
|
112
93
|
'cli.docs.installed': 'installed: {list}',
|
|
@@ -115,12 +96,19 @@ const CATALOG = {
|
|
|
115
96
|
'cli.docs.found': '{count} doc(s) for "{query}"',
|
|
116
97
|
'cli.docs.none': 'no framework doc matches "{query}"',
|
|
117
98
|
'cli.docs.unresolved': 'the installed framework packages could not be located',
|
|
99
|
+
'cli.drill.listed': '{count} drill(s); run one with x drill <name> --json',
|
|
100
|
+
'cli.drill.summary': '{held} of {ran} drill(s) held; {notRun} not run, its service undeclared',
|
|
118
101
|
'cli.errors.count': '{count} registered error code(s)',
|
|
119
102
|
'cli.errors.explained': '{code} — {title}',
|
|
103
|
+
'cli.exercise.none': 'this app registers no action, query or job: nothing to exercise',
|
|
104
|
+
'cli.exercise.summary':
|
|
105
|
+
'{total} primitive(s): {reached} reached, {refused} refused, {off} off, {unreached} unreached — {file}',
|
|
120
106
|
// One rendering of "this file was written", for every command that writes files — `x g`, its
|
|
121
107
|
// own `--dry-run`, and `x new`. Three copies of the same two characters is how a fourth writer
|
|
122
108
|
// arrives with a fifth marker; `--json` carries the paths themselves in `data.files`.
|
|
123
109
|
'cli.file.added': ' + {path}',
|
|
110
|
+
// Its pair, for the one generator that deletes: `x g sign-in --oidc` drops `x new`'s dev viewer.
|
|
111
|
+
'cli.file.removed': ' - {path}',
|
|
124
112
|
'cli.fix.clean': 'no boundary violation involves {file}',
|
|
125
113
|
// "nothing written", the same admission `cli.generate.planned` makes and for the same reason: a
|
|
126
114
|
// command called `fix` that only ever REPORTS teaches an agent to expect a repair and act as
|
|
@@ -135,6 +123,7 @@ const CATALOG = {
|
|
|
135
123
|
// file(s)" while `data.dryRun` said nothing had landed, so an agent branching on `summary`
|
|
136
124
|
// believed the files were on disk.
|
|
137
125
|
'cli.generate.planned': 'would write {count} file(s) for {kind} {name} — nothing written',
|
|
126
|
+
'cli.generate.chartAlertsPrinted': '{count} alert rule(s), as Prometheus rule groups',
|
|
138
127
|
'cli.i18n.added': 'added {locale} — {keys} key(s) seeded from {from}',
|
|
139
128
|
'cli.i18n.dynamic': '{count} dynamic t() call(s) the extractor cannot verify:',
|
|
140
129
|
'cli.i18n.gaps': '{missing} missing key(s) across {locales} locale(s)',
|
|
@@ -145,6 +134,9 @@ const CATALOG = {
|
|
|
145
134
|
'{count} locale-parity finding(s) across {locales} locale(s) — a slot or an untranslated copy against {reference}',
|
|
146
135
|
'cli.i18n.synced': 'synced {locale} from {from} — {added} key(s) added, {total} total',
|
|
147
136
|
'cli.i18n.unused': '{count} key(s) defined in {locale} and never used:',
|
|
137
|
+
'cli.mail.preview.missing':
|
|
138
|
+
'{id}: {missing} of {locales} locale(s) miss a key — see data.previews[].missing',
|
|
139
|
+
'cli.mail.preview.ok': '{id}: rendered in {locales} locale(s), every key resolved',
|
|
148
140
|
'cli.jobs.backfillNoCursor': 'no cursor yet',
|
|
149
141
|
'cli.jobs.cancelled': 'job {id} cancelled — {state}',
|
|
150
142
|
'cli.jobs.backfillRow': '{name} — {rows} row(s) so far, cursor {cursor}',
|
|
@@ -161,6 +153,7 @@ const CATALOG = {
|
|
|
161
153
|
'cli.jobs.removed': 'job {id} removed — it was {state}',
|
|
162
154
|
'cli.jobs.resumed': 'queue {queue} resumed',
|
|
163
155
|
'cli.jobs.retried': 'job {id} re-queued — {state}',
|
|
156
|
+
'cli.jobs.stepResolved': 'job {id} step {step} resolved — {status}; next: x jobs retry {id}',
|
|
164
157
|
'cli.jobs.shown': 'job {id} — {state}, attempt {attempt} of {attempts}',
|
|
165
158
|
'cli.manifest.blocked': 'manifest not written — {count} module(s) did not load',
|
|
166
159
|
'cli.manifest.fresh': 'manifest is fresh',
|
|
@@ -168,8 +161,10 @@ const CATALOG = {
|
|
|
168
161
|
'cli.manifest.wrote': 'manifest written to {path} ({routes} routes, {actions} actions)',
|
|
169
162
|
'cli.mcp.serving': 'mcp {transport} serving {tools} tools',
|
|
170
163
|
'cli.mcp.scopes': ' scopes {scopes}',
|
|
164
|
+
'cli.mcp.locked': 'mcp lock: {server} — {tools} tool(s) written to {file}',
|
|
165
|
+
'cli.mcp.lock_current': 'mcp lock: {server} matches {file}',
|
|
171
166
|
// `bun run setup` and nothing else: the scaffold ships it (`bin/setup.ts`), `README.md` and
|
|
172
|
-
//
|
|
167
|
+
// the generated `ci.yml` both name it, and it is the only spelling that is right on a fresh clone — it
|
|
173
168
|
// installs, writes `.env.development.local`, runs `x db gen "initial"` (the scaffold writes no
|
|
174
169
|
// migration, so the drift step is red until it has), migrates and seeds. The four-command line
|
|
175
170
|
// this replaced named `x dev` off a tree where nothing had installed the CLI yet, and skipped the
|
|
@@ -191,6 +186,8 @@ const CATALOG = {
|
|
|
191
186
|
// command is a broken one — the same split `Finding.fix` already makes.
|
|
192
187
|
'cli.new.wrote': ' {count} files in {dir}',
|
|
193
188
|
'cli.new.noRepository': ' no repository — {problem}',
|
|
189
|
+
// `git init` ran and the commit did not: `.git` exists, so "no repository" was false.
|
|
190
|
+
'cli.new.uncommitted': ' repository initialised, nothing committed — {problem}',
|
|
194
191
|
'cli.policy.count':
|
|
195
192
|
'{permissions} permission(s), {roles} role(s), {enforced} enforced by a declaration',
|
|
196
193
|
// One row per (declaration, actor) pair, never per role: a permission two declarations enforce
|
|
@@ -213,95 +210,6 @@ const CATALOG = {
|
|
|
213
210
|
'sealed {sealed} second-factor secret(s); {already} already sealed, {skipped} changed mid-run and left alone',
|
|
214
211
|
'cli.tasks.count': '{count} task(s)',
|
|
215
212
|
'cli.tasks.shown': '{name} — {cron} ({tz}), next {next}',
|
|
216
|
-
'cli.affected.count':
|
|
217
|
-
'{count} workspace(s) affected by {base}...HEAD, from {changed} changed file(s)',
|
|
218
|
-
'cli.affected.dirty':
|
|
219
|
-
' including the working tree (--dirty): every uncommitted change in this checkout, whoever made it',
|
|
220
|
-
'cli.affected.none': 'no workspace is affected by {base}...HEAD, from {changed} changed file(s)',
|
|
221
|
-
'cli.affected.rootWide':
|
|
222
|
-
' every workspace: {files} belongs to none of them and changes what all of them compile',
|
|
223
|
-
// `x shot` — the picture is the `lines`, the verdict is the artifact. The summary names the
|
|
224
|
-
// GATING fact, and a redirect comes first: a photograph of the sign-in page with every island
|
|
225
|
-
// missing reads as a bug in the app, and it is a bug in the capture.
|
|
226
|
-
'cli.shot.ok': '{route} clean — {islands} island(s) mounted, nothing logged and nothing threw',
|
|
227
|
-
'cli.shot.errors': '{route}: {errors} console error(s) — verdict.json names each one',
|
|
228
|
-
'cli.shot.redirected': '{route} redirected to {url} — the picture is not the route asked for',
|
|
229
|
-
// After the redirect and before everything else: a 404 page can be spotless, and a picture of
|
|
230
|
-
// "Not found" read as the route is the capture lying about its subject.
|
|
231
|
-
'cli.shot.status':
|
|
232
|
-
'{route} answered HTTP {status}, expected {expected} — the picture is that response, not the page',
|
|
233
|
-
'cli.shot.document': ' document HTTP {status}',
|
|
234
|
-
'cli.shot.documentUnknown': ' document status not seen — the driver recorded no response for it',
|
|
235
|
-
'cli.shot.server.booted': ' server booted for this shot on {url}',
|
|
236
|
-
'cli.shot.server.reused': ' server the x dev already running on {url}',
|
|
237
|
-
'cli.shot.canvas': ' canvas {width}x{height}',
|
|
238
|
-
'cli.shot.canvasUnreadable': ' canvas unreadable — {bytes} byte(s), not a decodable image',
|
|
239
|
-
'cli.shot.islands': ' islands {booted} of {declared} mounted ({strategies})',
|
|
240
|
-
'cli.shot.islandsUnknown': ' islands not counted — the page answered no probe',
|
|
241
|
-
// The failure a picture cannot show and a console count cannot see: a rejected mount promise
|
|
242
|
-
// calls no console method, so the FIRST one is named here rather than left to verdict.json.
|
|
243
|
-
'cli.shot.islandFailed': '{route}: {failed} island(s) failed to mount — {island}: {message}',
|
|
244
|
-
'cli.shot.network': ' network {requests} request(s), {refused} refused, {dropped} dropped',
|
|
245
|
-
'cli.shot.console': ' console {level}: {text}',
|
|
246
|
-
'cli.shot.threw': '{route}: {thrown} uncaught exception(s) — {first}',
|
|
247
|
-
'cli.shot.pageError': ' threw {message} {at}',
|
|
248
|
-
'cli.shot.picture': ' picture {path}',
|
|
249
|
-
'cli.shot.verdict': ' verdict {path}',
|
|
250
|
-
// `--viewport`: one route at several sizes. The summary is ONE size's verdict, so it says which
|
|
251
|
-
// size; each size line leads with its size and the per-size detail lines sit indented beneath.
|
|
252
|
-
'cli.shot.viewport.summary': '{size}: {summary}',
|
|
253
|
-
'cli.shot.viewport.line': '{size} {summary}',
|
|
254
|
-
// `--island`. A component capture reports per STATE, so the summary counts pictures and the
|
|
255
|
-
// lines name one state each; nothing here restates a fact `--json` does not carry.
|
|
256
|
-
'cli.shot.island.ok':
|
|
257
|
-
'{island} clean — {pictures} picture(s), every one mounted, nothing logged and nothing threw',
|
|
258
|
-
'cli.shot.island.failed':
|
|
259
|
-
'{island}: {taken} of {expected} declared picture(s) taken — verdict.json names each refusal',
|
|
260
|
-
'cli.shot.island.state': ' {state} {theme} {width}x{height} {file}',
|
|
261
|
-
'cli.shot.island.missing': ' missing {file} — no picture was taken for this declared state',
|
|
262
|
-
'cli.shot.island.picture': ' pictures {path}',
|
|
263
|
-
'cli.shot.island.verdict': ' verdict {path}',
|
|
264
|
-
'cli.shot.island.index': ' index {path}',
|
|
265
|
-
'cli.shot.island.blind.crop':
|
|
266
|
-
"the picture is the crop target and a thin margin around it — anything further out, including the space a component's own fault sits in, is outside the frame; content that overflows the box is recorded per state as overflow, never shown",
|
|
267
|
-
'cli.shot.island.blind.locale':
|
|
268
|
-
'toLocaleString() on a Date resolves its zone inside the engine — only an explicit timeZone is pinned by this harness',
|
|
269
|
-
'cli.ci.failed':
|
|
270
|
-
'{failed} of {runs} workflow run(s) on {branch} failed — {findings} finding(s) recovered from the log',
|
|
271
|
-
'cli.ci.green': 'every one of {runs} workflow run(s) on {branch} passed',
|
|
272
|
-
'cli.ci.job': ' {conclusion} {job} ({steps})',
|
|
273
|
-
'cli.ci.jobs.other': ' {count} other job(s) in this run',
|
|
274
|
-
'cli.ci.logs.empty': ' the failed step wrote no log — {url}',
|
|
275
|
-
/** The conclusion of a run GitHub has not finished — a value, not a column key. */
|
|
276
|
-
'cli.ci.pending': 'pending',
|
|
277
|
-
'cli.ci.run': '{conclusion} {workflow} {url}',
|
|
278
|
-
'cli.ci.running':
|
|
279
|
-
'{running} of {runs} workflow run(s) on {branch} has not finished — nothing has failed yet',
|
|
280
|
-
'cli.ci.tail': ' log tail, {job}:',
|
|
281
|
-
'cli.pr.body.truncated': ' … {hidden} more line(s) — re-run with --full',
|
|
282
|
-
/** The line of a thread whose anchor GitHub answers null for — a value, not a column key. */
|
|
283
|
-
'cli.pr.line.unknown': '-',
|
|
284
|
-
'cli.pr.replied': 'replied on thread {id}: {url}',
|
|
285
|
-
// Resolving closes a CONVERSATION. Whether the finding is fixed is a fact about the code that
|
|
286
|
-
// no GitHub mutation observes, and a summary saying "addressed" would assert one from the other.
|
|
287
|
-
'cli.pr.resolved':
|
|
288
|
-
'thread {id} is marked resolved on GitHub — that records the conversation, not that the finding is fixed',
|
|
289
|
-
'cli.pr.review.count':
|
|
290
|
-
'{unresolved} unresolved and {resolved} resolved review thread(s) on {repo}#{pr}',
|
|
291
|
-
'cli.pr.review.current': ' submitted against the current head {head}',
|
|
292
|
-
'cli.pr.review.decision': ' review: {decision} by {author} at {submitted}',
|
|
293
|
-
'cli.pr.review.none': 'no review thread is anchored to a line on {repo}#{pr}',
|
|
294
|
-
'cli.pr.review.stale':
|
|
295
|
-
' submitted against {commit}; the head is now {head} ({committed}) — this decision predates the current code',
|
|
296
|
-
'cli.pr.review.truncated':
|
|
297
|
-
' more than {count} threads — this is the first page, not the whole review',
|
|
298
|
-
'cli.pr.review.undecided': ' GitHub reports no review decision yet',
|
|
299
|
-
'cli.pr.thread.closed': ' resolved {path}:{line} {id}',
|
|
300
|
-
'cli.pr.thread.comment': ' {author} at {createdAt}',
|
|
301
|
-
'cli.pr.thread.more': ' {hidden} more comment(s) on this thread',
|
|
302
|
-
'cli.pr.thread.open': ' unresolved {path}:{line} {id}',
|
|
303
|
-
'cli.pr.thread.outdated':
|
|
304
|
-
' the diff has moved under this thread — {line} is where the comment was written',
|
|
305
213
|
'cli.test.fail': '{failed} of {workers} shard(s) failed',
|
|
306
214
|
'cli.test.affected.none': 'nothing is affected by {base}...HEAD — 0 test file(s) ran',
|
|
307
215
|
'cli.test.empty': 'no test file matches {selection} — 0 test file(s) ran (--allow-empty)',
|
|
@@ -311,56 +219,17 @@ const CATALOG = {
|
|
|
311
219
|
'cli.test.sampled': 'sampled {kept} of {total} {type} file(s)',
|
|
312
220
|
'cli.test.type.fail': '{type} — {failed} of {workers} shard(s) failed',
|
|
313
221
|
'cli.test.type.pass': '{type} — {files} test file(s) on {workers} worker(s) passed in {ms}ms',
|
|
314
|
-
// The banner a `--only` run carries, in front of whichever summary above it renders. Rendered
|
|
315
|
-
// output, so it lives here — `data.notAGateRun` is the machine marker, and a reader testing for
|
|
316
|
-
// one narrowed run reads that boolean rather than substring-matching this line.
|
|
317
|
-
'cli.verify.notAGateRun': 'NOT A GATE RUN — {summary}',
|
|
318
|
-
'cli.verify.pass': 'all {count} steps passed in {ms}ms',
|
|
319
|
-
'cli.verify.fail': '{failed} of {count} steps failed',
|
|
320
|
-
// A skipped step is not a passed one, so the two counts never share a sentence — and the skipped
|
|
321
|
-
// ones are named, because "which suite has nothing to run here?" is the question a green gate
|
|
322
|
-
// over a missing suite has to answer on its own line. Whole sentences per case rather than a
|
|
323
|
-
// clause the caller glues on: a catalog clause cannot know the sentence it lands in.
|
|
324
|
-
'cli.verify.passSkipped':
|
|
325
|
-
'{passed} of {count} steps passed in {ms}ms — {skipped} skipped: {names}',
|
|
326
|
-
'cli.verify.failSkipped': '{failed} of {count} steps failed — {skipped} skipped: {names}',
|
|
327
|
-
// The `errors` step's own coverage, in `output`: a scan without a parser reads most fix lines
|
|
328
|
-
// and not all of them, and a step that reports findings alone claims a completeness it lacks.
|
|
329
|
-
'cli.verify.fixCoverage': 'checked {checked} fix line(s), could not read {unreadable}',
|
|
330
|
-
// Why a skipped step is a dash when the step can say so: the suite exists and not one of its
|
|
331
|
-
// tests ran. Each test's own name carries the reason it skipped, and `bun test` prints only the
|
|
332
|
-
// counts — so the line says how many and `x test <type>` prints the names.
|
|
333
|
-
'cli.verify.allSkipped': 'found {skipped} test(s) and every one skipped itself',
|
|
334
|
-
'cli.verify.warnings': '{count} warning(s) — --verbose or --json lists them',
|
|
335
|
-
'cli.verify.slowest': 'slowest: {files}',
|
|
336
|
-
'cli.verify.isrQueryUndeclared':
|
|
337
|
-
"{file}: render 'isr' with no revalidate.query — its stored pages are keyed on the whole query string, so any visitor can mint one per request. Declare revalidate: { query: [] } (or the parameters the page varies on)",
|
|
338
|
-
'cli.verify.isrQueryOmitted':
|
|
339
|
-
'{file}: reads the query parameter "{name}", which revalidate.query does not list — the page is stored under a key without it, so every visitor gets the document rendered for whichever value arrived first. Declare query: [{declared}], or stop reading it',
|
|
340
|
-
// The other shape the same counts can take: a suite that reported no test at all, skipped ones
|
|
341
|
-
// included. Its own line and not `allSkipped` with a zero, because the repair differs — there is
|
|
342
|
-
// no skipped test to read a reason off, so the files the step selected hold no test.
|
|
343
|
-
'cli.verify.ranNothing': 'found no test to run in the file(s) it selected',
|
|
344
|
-
'cli.verify.merged': 'merged {parts} part(s) — {summary}',
|
|
345
|
-
// The heartbeat `verify-progress.ts` writes while a step is still running: `{steps}` is
|
|
346
|
-
// `unit (12m 3s), live (4m 0s)`, each step with the time since it started.
|
|
347
|
-
'cli.verify.stillRunning': 'still running: {steps}',
|
|
348
|
-
// The `unit` step's own measurement, in `output`: what the suite covered of the app's whole
|
|
349
|
-
// source tree, beside the floor it was held to. A shard says it measured a slice instead.
|
|
350
|
-
'cli.verify.coverage':
|
|
351
|
-
'coverage: {lines}% of lines, {funcs}% of functions over {files} source file(s) — floor {floor}',
|
|
352
|
-
'cli.verify.coverageDeferred':
|
|
353
|
-
'coverage: this shard measured its own slice — x verify merge folds every shard and judges the floor',
|
|
354
|
-
'cli.verify.serial': 'serial',
|
|
355
|
-
'cli.verify.workers': '{workers} workers',
|
|
356
222
|
'cli.clean.nothing': 'nothing to clean: no test leftovers under this app',
|
|
357
223
|
'cli.clean.removed': 'removed {paths} path(s) and {databases} probe database(s)',
|
|
358
224
|
'cli.clean.would':
|
|
359
225
|
'would remove {paths} path(s) and {databases} probe database(s) — --dry-run removed nothing',
|
|
360
226
|
'cli.env.checked': '{count} declared variable(s), all present and valid',
|
|
361
227
|
'cli.env.invalid': '{count} of {total} declared variable(s) missing or malformed',
|
|
228
|
+
'cli.env.checkedAsProduction': 'checked as production: no ULTIMATE_ENV named',
|
|
362
229
|
'cli.env.wrote': 'wrote {path} — {count} declared variable(s)',
|
|
363
230
|
'cli.env.fresh': '{path} already matches the declaration',
|
|
231
|
+
'cli.env.initialised': 'wrote {path} — {count} value(s), secrets generated and never printed',
|
|
232
|
+
'cli.env.unfilled': ' {key} is required and empty — set it in {path}',
|
|
364
233
|
'cli.secrets.init': 'sealed {path} — master key {kid}, and .gitignore now covers the key file',
|
|
365
234
|
'cli.secrets.deploy': ' carry the key into a deploy with {env}="$(cat {keyPath})"',
|
|
366
235
|
'cli.secrets.redeploy': ' set {env}="$(cat {keyPath})" in every deploy before the next release',
|
|
@@ -386,6 +255,12 @@ const CATALOG = {
|
|
|
386
255
|
'cli.secrets.dropped':
|
|
387
256
|
'dropped retired master key {kid} from {path} — {count} retired key(s) still declared',
|
|
388
257
|
'cli.secrets.ring': 'retired master key(s) that still open sealed values: {kids}',
|
|
258
|
+
...CLIENT_MESSAGES,
|
|
259
|
+
...REPRO_MESSAGES,
|
|
260
|
+
...DB_MESSAGES,
|
|
261
|
+
...SHOT_MESSAGES,
|
|
262
|
+
...UPGRADE_MESSAGES,
|
|
263
|
+
...GATE_MESSAGES,
|
|
389
264
|
} as const;
|
|
390
265
|
|
|
391
266
|
export type MessageKey = keyof typeof CATALOG;
|
package/src/metrics-endpoint.ts
CHANGED
|
@@ -37,6 +37,13 @@ import { quoteArg } from './shell-quote';
|
|
|
37
37
|
*/
|
|
38
38
|
export const DEFAULT_METRICS_PORT = 9090;
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* A container's entry is `bun apps/web/server.ts`, so `x dev` is no instruction there, and the
|
|
42
|
+
* refusal itself stays: a deployed scrape target must not move on its own.
|
|
43
|
+
*/
|
|
44
|
+
const DEPLOYED_PORT_FIX =
|
|
45
|
+
"set METRICS_PORT=<free port> in this role's environment, one per role on a host";
|
|
46
|
+
|
|
40
47
|
/**
|
|
41
48
|
* `X_PORT_IN_USE` is the code the CLI already registers for "this dev port is taken", and the
|
|
42
49
|
* scrape port is one — a synonym here would be a second code for one condition. The fix moves the
|
|
@@ -47,11 +54,13 @@ export const DEFAULT_METRICS_PORT = 9090;
|
|
|
47
54
|
* that is 65536, and an instruction that cannot run is the failure this code exists to end.
|
|
48
55
|
*/
|
|
49
56
|
export class MetricsPortInUseError extends UltimateError {
|
|
50
|
-
constructor(input: { port: number }) {
|
|
57
|
+
constructor(input: { port: number; dev: boolean }) {
|
|
51
58
|
super({
|
|
52
59
|
code: 'X_PORT_IN_USE',
|
|
53
60
|
cause: `the metrics port ${input.port} is already bound, so no role could open its scrape listener`,
|
|
54
|
-
fix:
|
|
61
|
+
fix: input.dev
|
|
62
|
+
? `METRICS_PORT=${quoteArg(String(neighbouringPort(input.port)))} x dev --json`
|
|
63
|
+
: DEPLOYED_PORT_FIX,
|
|
55
64
|
});
|
|
56
65
|
}
|
|
57
66
|
}
|
|
@@ -100,12 +109,17 @@ export function portApartFrom(reserved: readonly RolePort[], preferred: number):
|
|
|
100
109
|
}
|
|
101
110
|
|
|
102
111
|
export class MetricsPortSharedError extends UltimateError {
|
|
103
|
-
constructor(input: {
|
|
112
|
+
constructor(input: {
|
|
113
|
+
port: number;
|
|
114
|
+
with: RolePort;
|
|
115
|
+
reserved: readonly RolePort[];
|
|
116
|
+
dev: boolean;
|
|
117
|
+
}) {
|
|
104
118
|
const apart = portApartFrom(input.reserved, DEFAULT_METRICS_PORT);
|
|
105
119
|
super({
|
|
106
120
|
code: 'X_PORT_IN_USE',
|
|
107
121
|
cause: `the metrics port ${input.port} is this process's ${input.with.role} port ${input.with.port}, so two listeners would answer on one number`,
|
|
108
|
-
fix: `METRICS_PORT=${quoteArg(String(apart))} x dev --json
|
|
122
|
+
fix: input.dev ? `METRICS_PORT=${quoteArg(String(apart))} x dev --json` : DEPLOYED_PORT_FIX,
|
|
109
123
|
});
|
|
110
124
|
}
|
|
111
125
|
}
|
|
@@ -131,6 +145,8 @@ export interface MetricsEndpointOptions {
|
|
|
131
145
|
* A host that opens the listener itself and names none is labelled `unknown`.
|
|
132
146
|
*/
|
|
133
147
|
readonly role?: string;
|
|
148
|
+
/** Is this `x dev`? It picks the refusal's fix: a container has no `x dev` to rerun. Absent: no. */
|
|
149
|
+
readonly dev?: boolean;
|
|
134
150
|
/**
|
|
135
151
|
* What a taken port means. `refuse` (the default, and every container's): `X_PORT_IN_USE` —
|
|
136
152
|
* Prometheus is configured against the declared port, so listening anywhere else is a scrape
|
|
@@ -164,12 +180,13 @@ export function bindScrapePort<T>(
|
|
|
164
180
|
port: number,
|
|
165
181
|
whenTaken: 'refuse' | 'free',
|
|
166
182
|
bind: (port: number) => T,
|
|
183
|
+
dev = false,
|
|
167
184
|
): T {
|
|
168
185
|
try {
|
|
169
186
|
return bind(port);
|
|
170
187
|
} catch (error) {
|
|
171
188
|
if (!isAddressInUse(error)) throw error;
|
|
172
|
-
if (whenTaken === 'refuse' || port === 0) throw new MetricsPortInUseError({ port });
|
|
189
|
+
if (whenTaken === 'refuse' || port === 0) throw new MetricsPortInUseError({ port, dev });
|
|
173
190
|
logger.warn('ultimate metrics port taken', {
|
|
174
191
|
port,
|
|
175
192
|
cause: `the metrics port ${String(port)} is already bound — another x dev, or a Prometheus — so this process listens on a free one`,
|
|
@@ -231,6 +248,7 @@ export function startMetricsEndpoint(options: MetricsEndpointOptions = {}): Metr
|
|
|
231
248
|
port: asked,
|
|
232
249
|
with: shared,
|
|
233
250
|
reserved: options.reserved ?? [],
|
|
251
|
+
dev: options.dev === true,
|
|
234
252
|
});
|
|
235
253
|
}
|
|
236
254
|
const port = shared === undefined ? asked : 0;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// The generators a shape runs once its files are on disk. `x g sign-in` IS how an app gets sign-in —
|
|
2
|
+
// its pages, actions, catalog keys, `signInPath` and API registrations land together — so a shape
|
|
3
|
+
// that starts signed-in runs it, rather than an overlay re-spelling its output (a second way).
|
|
4
|
+
|
|
5
|
+
import { renderFixShellArg, UltimateError } from '@ultimat3/core';
|
|
6
|
+
import type { CommandContext } from './command';
|
|
7
|
+
import type { NewShape } from './new-shapes';
|
|
8
|
+
import type { CommandResult, JsonValue } from './output';
|
|
9
|
+
import { parseArgs } from './parse';
|
|
10
|
+
import { quoteArg } from './shell-quote';
|
|
11
|
+
|
|
12
|
+
/** A shape's generator refused or failed in the app `x new` just wrote: the framework's bug. */
|
|
13
|
+
export class NewShapeGenerateError extends UltimateError {
|
|
14
|
+
constructor(input: {
|
|
15
|
+
readonly shape: NewShape;
|
|
16
|
+
readonly argv: readonly string[];
|
|
17
|
+
readonly target: string;
|
|
18
|
+
readonly result: CommandResult;
|
|
19
|
+
}) {
|
|
20
|
+
const codes = (input.result.findings ?? []).map((finding) => finding.code).join(', ');
|
|
21
|
+
super({
|
|
22
|
+
code: 'X_NEW_SHAPE_GENERATE_FAILED',
|
|
23
|
+
cause: `x new --shape ${input.shape} ran x ${input.argv.join(' ')} in the app it wrote and it failed${codes === '' ? '' : ` (${codes})`}: ${input.result.summary}`,
|
|
24
|
+
// Each word through `quoteArg`: the command is the generator's argv, joined, never a template.
|
|
25
|
+
fix: [
|
|
26
|
+
`cd ${renderFixShellArg(input.target, '<the new app directory>')}`,
|
|
27
|
+
'&&',
|
|
28
|
+
...['bunx', 'x', ...input.argv, '--json'].map(quoteArg),
|
|
29
|
+
' # reproduces it; then bun test packages/cli/src/templates/shapes',
|
|
30
|
+
].join(' '),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Runs each of `generate` in `target`, in order, through the `x g` command itself, and answers the
|
|
37
|
+
* paths each wrote. The first that fails stops the run: a half-signed-in app is not a shape.
|
|
38
|
+
*/
|
|
39
|
+
export async function runShapeGenerators(
|
|
40
|
+
ctx: CommandContext,
|
|
41
|
+
shape: NewShape,
|
|
42
|
+
target: string,
|
|
43
|
+
generate: readonly (readonly string[])[],
|
|
44
|
+
): Promise<readonly string[]> {
|
|
45
|
+
if (generate.length === 0) return [];
|
|
46
|
+
// Lazy, as the registry loads every command body: `x new` with no shape never pays for `x g`.
|
|
47
|
+
const [{ generateCommand }, { SPECS }] = await Promise.all([
|
|
48
|
+
import('./cmd-generate'),
|
|
49
|
+
import('./registry'),
|
|
50
|
+
]);
|
|
51
|
+
const written: string[] = [];
|
|
52
|
+
for (const argv of generate) {
|
|
53
|
+
const args = parseArgs(['g', ...argv], SPECS);
|
|
54
|
+
const result = await generateCommand.run({ ...ctx, args, cwd: target });
|
|
55
|
+
if (!result.ok)
|
|
56
|
+
throw new NewShapeGenerateError({ shape, argv: ['g', ...argv], target, result });
|
|
57
|
+
written.push(...filesOf(result));
|
|
58
|
+
}
|
|
59
|
+
return written;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** A JSON object, by its type: `result.data` is the generator's own `JsonValue`, never `unknown`. */
|
|
63
|
+
const isJsonObject = (
|
|
64
|
+
value: JsonValue | undefined,
|
|
65
|
+
): value is { readonly [key: string]: JsonValue } =>
|
|
66
|
+
typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
67
|
+
|
|
68
|
+
const filesOf = (result: CommandResult): readonly string[] => {
|
|
69
|
+
const files = isJsonObject(result.data) ? result.data['files'] : undefined;
|
|
70
|
+
return Array.isArray(files) ? files.filter((file) => typeof file === 'string') : [];
|
|
71
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// `x new --shape <shape>`: the closed set of app shapes and the one reader of the flag. Apart from
|
|
2
|
+
// the templates (`templates/shapes/`) so `cmd-new-spec.ts` can list the names without loading them.
|
|
3
|
+
|
|
4
|
+
import { UltimateError } from '@ultimat3/core';
|
|
5
|
+
import type { ParsedArgs } from './parse';
|
|
6
|
+
import { flagString } from './parse';
|
|
7
|
+
import { quoteArg } from './shell-quote';
|
|
8
|
+
|
|
9
|
+
/** Every shape `x new` writes. A name here and no overlay in `templates/shapes/` fails its test. */
|
|
10
|
+
export const NEW_SHAPES = ['api', 'blog', 'dashboard'] as const;
|
|
11
|
+
|
|
12
|
+
export type NewShape = (typeof NEW_SHAPES)[number];
|
|
13
|
+
|
|
14
|
+
const isShape = (value: string): value is NewShape =>
|
|
15
|
+
(NEW_SHAPES as readonly string[]).includes(value);
|
|
16
|
+
|
|
17
|
+
/** `--shape` names nothing `x new` ships. */
|
|
18
|
+
export class NewShapeUnknownError extends UltimateError {
|
|
19
|
+
constructor(input: {
|
|
20
|
+
readonly shape: string;
|
|
21
|
+
readonly invocation: string;
|
|
22
|
+
readonly name: string;
|
|
23
|
+
}) {
|
|
24
|
+
super({
|
|
25
|
+
code: 'X_NEW_SHAPE_UNKNOWN',
|
|
26
|
+
cause: `--shape ${quoteArg(input.shape)} is not a shape x new writes; the shapes are ${NEW_SHAPES.join(', ')}`,
|
|
27
|
+
fix: `${input.invocation} ${quoteArg(input.name)} --shape ${NEW_SHAPES[0]} # every shape: bunx @ultimat3/cli help new`,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** `--shape` with `--no-example`: a shape is its own example, so the pair names two apps. */
|
|
33
|
+
export class NewShapeConflictError extends UltimateError {
|
|
34
|
+
constructor(input: {
|
|
35
|
+
readonly shape: NewShape;
|
|
36
|
+
readonly invocation: string;
|
|
37
|
+
readonly name: string;
|
|
38
|
+
}) {
|
|
39
|
+
super({
|
|
40
|
+
code: 'X_NEW_SHAPE_CONFLICT',
|
|
41
|
+
cause: `--shape ${input.shape} and --no-example ask for two different apps: a shape is its own example, and --no-example is the default shape with an empty app/`,
|
|
42
|
+
fix: `${input.invocation} ${quoteArg(input.name)} --shape ${input.shape}`,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The shape the caller asked for, `undefined` for the default scaffold. Refuses an unknown name and
|
|
49
|
+
* the `--no-example` pairing before anything is planned, so a refusal writes no file.
|
|
50
|
+
*/
|
|
51
|
+
export function readShape(
|
|
52
|
+
args: ParsedArgs,
|
|
53
|
+
invocation: string,
|
|
54
|
+
name: string,
|
|
55
|
+
): NewShape | undefined {
|
|
56
|
+
const raw = flagString(args, 'shape');
|
|
57
|
+
if (raw === undefined) return undefined;
|
|
58
|
+
if (!isShape(raw)) throw new NewShapeUnknownError({ shape: raw, invocation, name });
|
|
59
|
+
if (args.flags.get('example') === false) {
|
|
60
|
+
throw new NewShapeConflictError({ shape: raw, invocation, name });
|
|
61
|
+
}
|
|
62
|
+
return raw;
|
|
63
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// The neutral form `openapi-compile.ts` reduces an OpenAPI document to, and both client emitters
|
|
2
|
+
// read: named types, operations, and nothing that still needs the document. Types only — the
|
|
3
|
+
// compiler is the one reader of OpenAPI, the emitters never see a JSON pointer they must resolve.
|
|
4
|
+
|
|
5
|
+
/** One value shape. `unknown` is a schema that states nothing (`{}`), never a construct skipped. */
|
|
6
|
+
export type ClientType =
|
|
7
|
+
| { readonly kind: 'unknown' }
|
|
8
|
+
| { readonly kind: 'string' }
|
|
9
|
+
| { readonly kind: 'number'; readonly integer: boolean }
|
|
10
|
+
| { readonly kind: 'boolean' }
|
|
11
|
+
| { readonly kind: 'null' }
|
|
12
|
+
| { readonly kind: 'literal'; readonly value: string | number | boolean }
|
|
13
|
+
| { readonly kind: 'array'; readonly items: ClientType }
|
|
14
|
+
| { readonly kind: 'object'; readonly fields: readonly ClientField[] }
|
|
15
|
+
| { readonly kind: 'record'; readonly values: ClientType }
|
|
16
|
+
| { readonly kind: 'union'; readonly members: readonly ClientType[] }
|
|
17
|
+
| { readonly kind: 'ref'; readonly name: string };
|
|
18
|
+
|
|
19
|
+
export type ClientObject = Extract<ClientType, { readonly kind: 'object' }>;
|
|
20
|
+
|
|
21
|
+
export interface ClientField {
|
|
22
|
+
readonly name: string;
|
|
23
|
+
readonly type: ClientType;
|
|
24
|
+
readonly required: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type ClientMethod = 'GET' | 'HEAD' | 'PUT' | 'DELETE' | 'POST' | 'PATCH' | 'OPTIONS';
|
|
28
|
+
|
|
29
|
+
export interface ClientParameter {
|
|
30
|
+
readonly name: string;
|
|
31
|
+
readonly in: 'path' | 'query' | 'header';
|
|
32
|
+
readonly required: boolean;
|
|
33
|
+
readonly type: ClientType;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* How the request body reaches the input: `fields` spreads an object body's fields into the input
|
|
38
|
+
* beside the parameters (a parameter of the same name reads the same field), `value` puts any
|
|
39
|
+
* other body under `input.body`.
|
|
40
|
+
*/
|
|
41
|
+
export type ClientBody =
|
|
42
|
+
| { readonly mode: 'fields'; readonly type: ClientType; readonly required: boolean }
|
|
43
|
+
| { readonly mode: 'value'; readonly type: ClientType; readonly required: boolean };
|
|
44
|
+
|
|
45
|
+
export interface ClientOperation {
|
|
46
|
+
/** A TypeScript identifier from `operationId` — stable, so a rename is a contract change. */
|
|
47
|
+
readonly name: string;
|
|
48
|
+
readonly method: ClientMethod;
|
|
49
|
+
/** The template as the document states it: `/invoices/{invoiceId}`. */
|
|
50
|
+
readonly path: string;
|
|
51
|
+
readonly tags: readonly string[];
|
|
52
|
+
readonly summary?: string | undefined;
|
|
53
|
+
readonly parameters: readonly ClientParameter[];
|
|
54
|
+
readonly body?: ClientBody | undefined;
|
|
55
|
+
/** The 2xx JSON body; `undefined` when the success answer has none. */
|
|
56
|
+
readonly response?: ClientType | undefined;
|
|
57
|
+
/** Codes a refusal may carry, read from `x-ultimate-errors` on the non-2xx answers. */
|
|
58
|
+
readonly refusals: readonly string[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface CompiledDocument {
|
|
62
|
+
readonly title: string;
|
|
63
|
+
readonly version: string;
|
|
64
|
+
/** Named component schemas, sorted by name — only those an operation kept reaches. */
|
|
65
|
+
readonly types: ReadonlyMap<string, ClientType>;
|
|
66
|
+
/** Sorted by name. */
|
|
67
|
+
readonly operations: readonly ClientOperation[];
|
|
68
|
+
}
|