@vibesdotdev/infra-deploy 0.0.1 → 1.0.0-rc.4
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/LICENSE +18 -0
- package/README.md +79 -118
- package/dist/cli/alerts/infra-alerts-create.cli-command.descriptor.d.ts +48 -35
- package/dist/cli/alerts/infra-alerts-create.cli-command.descriptor.d.ts.map +1 -1
- package/dist/cli/alerts/infra-alerts-create.cli-command.descriptor.js +1 -1
- package/dist/cli/alerts/infra-alerts-create.cli-command.descriptor.js.map +1 -1
- package/dist/cli/alerts/infra-alerts-create.cli-command.impl.d.ts +2 -1
- package/dist/cli/alerts/infra-alerts-create.cli-command.impl.d.ts.map +1 -1
- package/dist/cli/alerts/infra-alerts-create.cli-command.impl.js +31 -21
- package/dist/cli/alerts/infra-alerts-create.cli-command.impl.js.map +1 -1
- package/dist/cli/alerts/infra-alerts-delete.cli-command.descriptor.d.ts +26 -27
- package/dist/cli/alerts/infra-alerts-delete.cli-command.descriptor.d.ts.map +1 -1
- package/dist/cli/alerts/infra-alerts-delete.cli-command.descriptor.js +1 -1
- package/dist/cli/alerts/infra-alerts-delete.cli-command.descriptor.js.map +1 -1
- package/dist/cli/alerts/infra-alerts-delete.cli-command.impl.d.ts +2 -1
- package/dist/cli/alerts/infra-alerts-delete.cli-command.impl.d.ts.map +1 -1
- package/dist/cli/alerts/infra-alerts-delete.cli-command.impl.js +11 -11
- package/dist/cli/alerts/infra-alerts-delete.cli-command.impl.js.map +1 -1
- package/dist/cli/alerts/infra-alerts-list.cli-command.descriptor.d.ts +19 -17
- package/dist/cli/alerts/infra-alerts-list.cli-command.descriptor.d.ts.map +1 -1
- package/dist/cli/alerts/infra-alerts-list.cli-command.descriptor.js +1 -1
- package/dist/cli/alerts/infra-alerts-list.cli-command.descriptor.js.map +1 -1
- package/dist/cli/alerts/infra-alerts-list.cli-command.impl.d.ts +2 -1
- package/dist/cli/alerts/infra-alerts-list.cli-command.impl.d.ts.map +1 -1
- package/dist/cli/alerts/infra-alerts-list.cli-command.impl.js +15 -14
- package/dist/cli/alerts/infra-alerts-list.cli-command.impl.js.map +1 -1
- package/dist/cli/alerts/infra-alerts.cli-group.descriptor.d.ts +8 -8
- package/dist/cli/alerts/infra-alerts.cli-group.descriptor.js +1 -1
- package/dist/cli/alerts/infra-alerts.cli-group.descriptor.js.map +1 -1
- package/dist/cli/audit/infra-audit.cli-command.descriptor.d.ts +24 -17
- package/dist/cli/audit/infra-audit.cli-command.descriptor.d.ts.map +1 -1
- package/dist/cli/audit/infra-audit.cli-command.descriptor.js +8 -3
- package/dist/cli/audit/infra-audit.cli-command.descriptor.js.map +1 -1
- package/dist/cli/audit/infra-audit.cli-command.impl.d.ts +164 -1
- package/dist/cli/audit/infra-audit.cli-command.impl.d.ts.map +1 -1
- package/dist/cli/audit/infra-audit.cli-command.impl.js +557 -76
- package/dist/cli/audit/infra-audit.cli-command.impl.js.map +1 -1
- package/dist/cli/db/infra-db.cli-command.descriptor.d.ts +49 -0
- package/dist/cli/db/infra-db.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/db/infra-db.cli-command.descriptor.js +60 -0
- package/dist/cli/db/infra-db.cli-command.descriptor.js.map +1 -0
- package/dist/cli/db/infra-db.cli-command.impl.d.ts +30 -0
- package/dist/cli/db/infra-db.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/db/infra-db.cli-command.impl.js +167 -0
- package/dist/cli/db/infra-db.cli-command.impl.js.map +1 -0
- package/dist/cli/dns/infra-dns-activate.cli-command.impl.d.ts +6 -0
- package/dist/cli/dns/infra-dns-activate.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/dns/infra-dns-activate.cli-command.impl.js +53 -0
- package/dist/cli/dns/infra-dns-activate.cli-command.impl.js.map +1 -0
- package/dist/cli/dns/infra-dns-delete.cli-command.descriptor.d.ts +41 -0
- package/dist/cli/dns/infra-dns-delete.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/dns/infra-dns-delete.cli-command.descriptor.js +50 -0
- package/dist/cli/dns/infra-dns-delete.cli-command.descriptor.js.map +1 -0
- package/dist/cli/dns/infra-dns-delete.cli-command.impl.d.ts +12 -0
- package/dist/cli/dns/infra-dns-delete.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/dns/infra-dns-delete.cli-command.impl.js +70 -0
- package/dist/cli/dns/infra-dns-delete.cli-command.impl.js.map +1 -0
- package/dist/cli/dns/infra-dns-list.cli-command.descriptor.d.ts +32 -0
- package/dist/cli/dns/infra-dns-list.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/dns/infra-dns-list.cli-command.descriptor.js +39 -0
- package/dist/cli/dns/infra-dns-list.cli-command.descriptor.js.map +1 -0
- package/dist/cli/dns/infra-dns-list.cli-command.impl.d.ts +17 -0
- package/dist/cli/dns/infra-dns-list.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/dns/infra-dns-list.cli-command.impl.js +66 -0
- package/dist/cli/dns/infra-dns-list.cli-command.impl.js.map +1 -0
- package/dist/cli/dns/infra-dns-set.cli-command.descriptor.d.ts +58 -0
- package/dist/cli/dns/infra-dns-set.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/dns/infra-dns-set.cli-command.descriptor.js +72 -0
- package/dist/cli/dns/infra-dns-set.cli-command.descriptor.js.map +1 -0
- package/dist/cli/dns/infra-dns-set.cli-command.impl.d.ts +14 -0
- package/dist/cli/dns/infra-dns-set.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/dns/infra-dns-set.cli-command.impl.js +110 -0
- package/dist/cli/dns/infra-dns-set.cli-command.impl.js.map +1 -0
- package/dist/cli/dns/infra-dns.cli-group.descriptor.d.ts +41 -0
- package/dist/cli/dns/infra-dns.cli-group.descriptor.d.ts.map +1 -0
- package/dist/cli/dns/infra-dns.cli-group.descriptor.js +47 -0
- package/dist/cli/dns/infra-dns.cli-group.descriptor.js.map +1 -0
- package/dist/cli/dns/resolve-dns-provider.d.ts +24 -0
- package/dist/cli/dns/resolve-dns-provider.d.ts.map +1 -0
- package/dist/cli/dns/resolve-dns-provider.js +62 -0
- package/dist/cli/dns/resolve-dns-provider.js.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls-delete.cli-command.descriptor.d.ts +37 -0
- package/dist/cli/edge-tls/infra-edge-tls-delete.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls-delete.cli-command.descriptor.js +45 -0
- package/dist/cli/edge-tls/infra-edge-tls-delete.cli-command.descriptor.js.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls-delete.cli-command.impl.d.ts +15 -0
- package/dist/cli/edge-tls/infra-edge-tls-delete.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls-delete.cli-command.impl.js +78 -0
- package/dist/cli/edge-tls/infra-edge-tls-delete.cli-command.impl.js.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls-list.cli-command.descriptor.d.ts +29 -0
- package/dist/cli/edge-tls/infra-edge-tls-list.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls-list.cli-command.descriptor.js +35 -0
- package/dist/cli/edge-tls/infra-edge-tls-list.cli-command.descriptor.js.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls-list.cli-command.impl.d.ts +19 -0
- package/dist/cli/edge-tls/infra-edge-tls-list.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls-list.cli-command.impl.js +73 -0
- package/dist/cli/edge-tls/infra-edge-tls-list.cli-command.impl.js.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls-set-zone-default.cli-command.descriptor.d.ts +41 -0
- package/dist/cli/edge-tls/infra-edge-tls-set-zone-default.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls-set-zone-default.cli-command.descriptor.js +50 -0
- package/dist/cli/edge-tls/infra-edge-tls-set-zone-default.cli-command.descriptor.js.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls-set-zone-default.cli-command.impl.d.ts +16 -0
- package/dist/cli/edge-tls/infra-edge-tls-set-zone-default.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls-set-zone-default.cli-command.impl.js +81 -0
- package/dist/cli/edge-tls/infra-edge-tls-set-zone-default.cli-command.impl.js.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls-set.cli-command.descriptor.d.ts +40 -0
- package/dist/cli/edge-tls/infra-edge-tls-set.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls-set.cli-command.descriptor.js +49 -0
- package/dist/cli/edge-tls/infra-edge-tls-set.cli-command.descriptor.js.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls-set.cli-command.impl.d.ts +14 -0
- package/dist/cli/edge-tls/infra-edge-tls-set.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls-set.cli-command.impl.js +78 -0
- package/dist/cli/edge-tls/infra-edge-tls-set.cli-command.impl.js.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls.cli-group.descriptor.d.ts +12 -0
- package/dist/cli/edge-tls/infra-edge-tls.cli-group.descriptor.d.ts.map +1 -0
- package/dist/cli/edge-tls/infra-edge-tls.cli-group.descriptor.js +12 -0
- package/dist/cli/edge-tls/infra-edge-tls.cli-group.descriptor.js.map +1 -0
- package/dist/cli/edge-tls/resolve-edge-tls-provider.d.ts +25 -0
- package/dist/cli/edge-tls/resolve-edge-tls-provider.d.ts.map +1 -0
- package/dist/cli/edge-tls/resolve-edge-tls-provider.js +74 -0
- package/dist/cli/edge-tls/resolve-edge-tls-provider.js.map +1 -0
- package/dist/cli/infra-deploy.cli-group.descriptor.d.ts +8 -8
- package/dist/cli/infra-deploy.cli-group.descriptor.js +1 -1
- package/dist/cli/infra-deploy.cli-group.descriptor.js.map +1 -1
- package/dist/cli/infra.cli-group.descriptor.d.ts +7 -7
- package/dist/cli/infra.cli-group.descriptor.js +1 -1
- package/dist/cli/infra.cli-group.descriptor.js.map +1 -1
- package/dist/cli/inventory/infra-inventory.cli-command.descriptor.d.ts +26 -0
- package/dist/cli/inventory/infra-inventory.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/inventory/infra-inventory.cli-command.descriptor.js +18 -0
- package/dist/cli/inventory/infra-inventory.cli-command.descriptor.js.map +1 -0
- package/dist/cli/inventory/infra-inventory.cli-command.impl.d.ts +18 -0
- package/dist/cli/inventory/infra-inventory.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/inventory/infra-inventory.cli-command.impl.js +204 -0
- package/dist/cli/inventory/infra-inventory.cli-command.impl.js.map +1 -0
- package/dist/cli/list/infra-deploy.list.cli-command.descriptor.d.ts +40 -30
- package/dist/cli/list/infra-deploy.list.cli-command.descriptor.d.ts.map +1 -1
- package/dist/cli/list/infra-deploy.list.cli-command.descriptor.js +7 -3
- package/dist/cli/list/infra-deploy.list.cli-command.descriptor.js.map +1 -1
- package/dist/cli/list/infra-deploy.list.cli-command.impl.d.ts +35 -1
- package/dist/cli/list/infra-deploy.list.cli-command.impl.d.ts.map +1 -1
- package/dist/cli/list/infra-deploy.list.cli-command.impl.js +202 -20
- package/dist/cli/list/infra-deploy.list.cli-command.impl.js.map +1 -1
- package/dist/cli/logs/infra-logs.cli-command.descriptor.d.ts +113 -35
- package/dist/cli/logs/infra-logs.cli-command.descriptor.d.ts.map +1 -1
- package/dist/cli/logs/infra-logs.cli-command.descriptor.js +87 -3
- package/dist/cli/logs/infra-logs.cli-command.descriptor.js.map +1 -1
- package/dist/cli/logs/infra-logs.cli-command.impl.d.ts +61 -1
- package/dist/cli/logs/infra-logs.cli-command.impl.d.ts.map +1 -1
- package/dist/cli/logs/infra-logs.cli-command.impl.js +1161 -108
- package/dist/cli/logs/infra-logs.cli-command.impl.js.map +1 -1
- package/dist/cli/logs/stream-worker-tail.d.ts +7 -2
- package/dist/cli/logs/stream-worker-tail.d.ts.map +1 -1
- package/dist/cli/logs/stream-worker-tail.js +280 -113
- package/dist/cli/logs/stream-worker-tail.js.map +1 -1
- package/dist/cli/migrate/infra-deploy.migrate.cli-command.descriptor.d.ts +62 -0
- package/dist/cli/migrate/infra-deploy.migrate.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/migrate/infra-deploy.migrate.cli-command.descriptor.js +79 -0
- package/dist/cli/migrate/infra-deploy.migrate.cli-command.descriptor.js.map +1 -0
- package/dist/cli/migrate/infra-deploy.migrate.cli-command.impl.d.ts +8 -0
- package/dist/cli/migrate/infra-deploy.migrate.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/migrate/infra-deploy.migrate.cli-command.impl.js +105 -0
- package/dist/cli/migrate/infra-deploy.migrate.cli-command.impl.js.map +1 -0
- package/dist/cli/npm/infra-npm-mirror.cli-command.descriptor.d.ts +34 -0
- package/dist/cli/npm/infra-npm-mirror.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/npm/infra-npm-mirror.cli-command.descriptor.js +42 -0
- package/dist/cli/npm/infra-npm-mirror.cli-command.descriptor.js.map +1 -0
- package/dist/cli/npm/infra-npm-mirror.cli-command.impl.d.ts +13 -0
- package/dist/cli/npm/infra-npm-mirror.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/npm/infra-npm-mirror.cli-command.impl.js +82 -0
- package/dist/cli/npm/infra-npm-mirror.cli-command.impl.js.map +1 -0
- package/dist/cli/npm/infra-npm-publish.cli-command.descriptor.d.ts +80 -23
- package/dist/cli/npm/infra-npm-publish.cli-command.descriptor.d.ts.map +1 -1
- package/dist/cli/npm/infra-npm-publish.cli-command.descriptor.js +48 -16
- package/dist/cli/npm/infra-npm-publish.cli-command.descriptor.js.map +1 -1
- package/dist/cli/npm/infra-npm-publish.cli-command.impl.d.ts +47 -1
- package/dist/cli/npm/infra-npm-publish.cli-command.impl.d.ts.map +1 -1
- package/dist/cli/npm/infra-npm-publish.cli-command.impl.js +532 -351
- package/dist/cli/npm/infra-npm-publish.cli-command.impl.js.map +1 -1
- package/dist/cli/npm/infra-npm-reconcile-latest.cli-command.descriptor.d.ts +28 -0
- package/dist/cli/npm/infra-npm-reconcile-latest.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/npm/infra-npm-reconcile-latest.cli-command.descriptor.js +34 -0
- package/dist/cli/npm/infra-npm-reconcile-latest.cli-command.descriptor.js.map +1 -0
- package/dist/cli/npm/infra-npm-reconcile-latest.cli-command.impl.d.ts +12 -0
- package/dist/cli/npm/infra-npm-reconcile-latest.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/npm/infra-npm-reconcile-latest.cli-command.impl.js +134 -0
- package/dist/cli/npm/infra-npm-reconcile-latest.cli-command.impl.js.map +1 -0
- package/dist/cli/npm/infra-npm.cli-group.descriptor.d.ts +8 -8
- package/dist/cli/npm/infra-npm.cli-group.descriptor.js +1 -1
- package/dist/cli/npm/infra-npm.cli-group.descriptor.js.map +1 -1
- package/dist/cli/npm/mirror-registry-artifacts.d.ts +64 -0
- package/dist/cli/npm/mirror-registry-artifacts.d.ts.map +1 -0
- package/dist/cli/npm/mirror-registry-artifacts.js +114 -0
- package/dist/cli/npm/mirror-registry-artifacts.js.map +1 -0
- package/dist/cli/npm/publish/command-options.d.ts +24 -0
- package/dist/cli/npm/publish/command-options.d.ts.map +1 -0
- package/dist/cli/npm/publish/command-options.js +85 -0
- package/dist/cli/npm/publish/command-options.js.map +1 -0
- package/dist/cli/npm/publish/concurrency.d.ts +19 -0
- package/dist/cli/npm/publish/concurrency.d.ts.map +1 -0
- package/dist/cli/npm/publish/concurrency.js +60 -0
- package/dist/cli/npm/publish/concurrency.js.map +1 -0
- package/dist/cli/npm/publish/consumer-smoke.d.ts +83 -0
- package/dist/cli/npm/publish/consumer-smoke.d.ts.map +1 -0
- package/dist/cli/npm/publish/consumer-smoke.js +371 -0
- package/dist/cli/npm/publish/consumer-smoke.js.map +1 -0
- package/dist/cli/npm/publish/consumer-surfaces.d.ts +81 -0
- package/dist/cli/npm/publish/consumer-surfaces.d.ts.map +1 -0
- package/dist/cli/npm/publish/consumer-surfaces.js +199 -0
- package/dist/cli/npm/publish/consumer-surfaces.js.map +1 -0
- package/dist/cli/npm/publish/external-requirements.d.ts +171 -0
- package/dist/cli/npm/publish/external-requirements.d.ts.map +1 -0
- package/dist/cli/npm/publish/external-requirements.js +365 -0
- package/dist/cli/npm/publish/external-requirements.js.map +1 -0
- package/dist/cli/npm/publish/forge-source-identity.d.ts +64 -0
- package/dist/cli/npm/publish/forge-source-identity.d.ts.map +1 -0
- package/dist/cli/npm/publish/forge-source-identity.js +201 -0
- package/dist/cli/npm/publish/forge-source-identity.js.map +1 -0
- package/dist/cli/npm/publish/fs-paths.d.ts +25 -0
- package/dist/cli/npm/publish/fs-paths.d.ts.map +1 -0
- package/dist/cli/npm/publish/fs-paths.js +87 -0
- package/dist/cli/npm/publish/fs-paths.js.map +1 -0
- package/dist/cli/npm/publish/lease-preflight-answer.d.ts +17 -0
- package/dist/cli/npm/publish/lease-preflight-answer.d.ts.map +1 -0
- package/dist/cli/npm/publish/lease-preflight-answer.js +35 -0
- package/dist/cli/npm/publish/lease-preflight-answer.js.map +1 -0
- package/dist/cli/npm/publish/lease-preflight.d.ts +92 -0
- package/dist/cli/npm/publish/lease-preflight.d.ts.map +1 -0
- package/dist/cli/npm/publish/lease-preflight.js +205 -0
- package/dist/cli/npm/publish/lease-preflight.js.map +1 -0
- package/dist/cli/npm/publish/managed-lease.d.ts +110 -0
- package/dist/cli/npm/publish/managed-lease.d.ts.map +1 -0
- package/dist/cli/npm/publish/managed-lease.js +191 -0
- package/dist/cli/npm/publish/managed-lease.js.map +1 -0
- package/dist/cli/npm/publish/npm-exec.d.ts +148 -0
- package/dist/cli/npm/publish/npm-exec.d.ts.map +1 -0
- package/dist/cli/npm/publish/npm-exec.js +311 -0
- package/dist/cli/npm/publish/npm-exec.js.map +1 -0
- package/dist/cli/npm/publish/package-artifact.d.ts +107 -0
- package/dist/cli/npm/publish/package-artifact.d.ts.map +1 -0
- package/dist/cli/npm/publish/package-artifact.js +418 -0
- package/dist/cli/npm/publish/package-artifact.js.map +1 -0
- package/dist/cli/npm/publish/platform-registry.d.ts +76 -0
- package/dist/cli/npm/publish/platform-registry.d.ts.map +1 -0
- package/dist/cli/npm/publish/platform-registry.js +324 -0
- package/dist/cli/npm/publish/platform-registry.js.map +1 -0
- package/dist/cli/npm/publish/proof-external-closure.d.ts +139 -0
- package/dist/cli/npm/publish/proof-external-closure.d.ts.map +1 -0
- package/dist/cli/npm/publish/proof-external-closure.js +339 -0
- package/dist/cli/npm/publish/proof-external-closure.js.map +1 -0
- package/dist/cli/npm/publish/proof-installed-graph.d.ts +26 -0
- package/dist/cli/npm/publish/proof-installed-graph.d.ts.map +1 -0
- package/dist/cli/npm/publish/proof-installed-graph.js +229 -0
- package/dist/cli/npm/publish/proof-installed-graph.js.map +1 -0
- package/dist/cli/npm/publish/proof-registry.d.ts +74 -0
- package/dist/cli/npm/publish/proof-registry.d.ts.map +1 -0
- package/dist/cli/npm/publish/proof-registry.js +335 -0
- package/dist/cli/npm/publish/proof-registry.js.map +1 -0
- package/dist/cli/npm/publish/proof-source-credentials.d.ts +42 -0
- package/dist/cli/npm/publish/proof-source-credentials.d.ts.map +1 -0
- package/dist/cli/npm/publish/proof-source-credentials.js +129 -0
- package/dist/cli/npm/publish/proof-source-credentials.js.map +1 -0
- package/dist/cli/npm/publish/publish-package.d.ts +66 -0
- package/dist/cli/npm/publish/publish-package.d.ts.map +1 -0
- package/dist/cli/npm/publish/publish-package.js +544 -0
- package/dist/cli/npm/publish/publish-package.js.map +1 -0
- package/dist/cli/npm/publish/publisher-manifest.d.ts +173 -0
- package/dist/cli/npm/publish/publisher-manifest.d.ts.map +1 -0
- package/dist/cli/npm/publish/publisher-manifest.js +79 -0
- package/dist/cli/npm/publish/publisher-manifest.js.map +1 -0
- package/dist/cli/npm/publish/rc-major-exception.d.ts +20 -0
- package/dist/cli/npm/publish/rc-major-exception.d.ts.map +1 -0
- package/dist/cli/npm/publish/rc-major-exception.js +46 -0
- package/dist/cli/npm/publish/rc-major-exception.js.map +1 -0
- package/dist/cli/npm/publish/registry-availability.d.ts +150 -0
- package/dist/cli/npm/publish/registry-availability.d.ts.map +1 -0
- package/dist/cli/npm/publish/registry-availability.js +570 -0
- package/dist/cli/npm/publish/registry-availability.js.map +1 -0
- package/dist/cli/npm/publish/registry-credentials.d.ts +108 -0
- package/dist/cli/npm/publish/registry-credentials.d.ts.map +1 -0
- package/dist/cli/npm/publish/registry-credentials.js +290 -0
- package/dist/cli/npm/publish/registry-credentials.js.map +1 -0
- package/dist/cli/npm/publish/registry-reads.d.ts +42 -0
- package/dist/cli/npm/publish/registry-reads.d.ts.map +1 -0
- package/dist/cli/npm/publish/registry-reads.js +164 -0
- package/dist/cli/npm/publish/registry-reads.js.map +1 -0
- package/dist/cli/npm/publish/release-build.d.ts +46 -0
- package/dist/cli/npm/publish/release-build.d.ts.map +1 -0
- package/dist/cli/npm/publish/release-build.js +140 -0
- package/dist/cli/npm/publish/release-build.js.map +1 -0
- package/dist/cli/npm/publish/release-consumer-proof.d.ts +70 -0
- package/dist/cli/npm/publish/release-consumer-proof.d.ts.map +1 -0
- package/dist/cli/npm/publish/release-consumer-proof.js +397 -0
- package/dist/cli/npm/publish/release-consumer-proof.js.map +1 -0
- package/dist/cli/npm/publish/release-executor.d.ts +46 -0
- package/dist/cli/npm/publish/release-executor.d.ts.map +1 -0
- package/dist/cli/npm/publish/release-executor.js +181 -0
- package/dist/cli/npm/publish/release-executor.js.map +1 -0
- package/dist/cli/npm/publish/release-grants.client.impl.d.ts +10 -0
- package/dist/cli/npm/publish/release-grants.client.impl.d.ts.map +1 -0
- package/dist/cli/npm/publish/release-grants.client.impl.js +63 -0
- package/dist/cli/npm/publish/release-grants.client.impl.js.map +1 -0
- package/dist/cli/npm/publish/release-jobs.client.d.ts +39 -0
- package/dist/cli/npm/publish/release-jobs.client.d.ts.map +1 -0
- package/dist/cli/npm/publish/release-jobs.client.js +124 -0
- package/dist/cli/npm/publish/release-jobs.client.js.map +1 -0
- package/dist/cli/npm/publish/release-jobs.consumer.plugin.d.ts +20 -0
- package/dist/cli/npm/publish/release-jobs.consumer.plugin.d.ts.map +1 -0
- package/dist/cli/npm/publish/release-jobs.consumer.plugin.js +82 -0
- package/dist/cli/npm/publish/release-jobs.consumer.plugin.js.map +1 -0
- package/dist/cli/npm/publish/release-jobs.impl.d.ts +15 -0
- package/dist/cli/npm/publish/release-jobs.impl.d.ts.map +1 -0
- package/dist/cli/npm/publish/release-jobs.impl.js +42 -0
- package/dist/cli/npm/publish/release-jobs.impl.js.map +1 -0
- package/dist/cli/npm/publish/release-lock.d.ts +143 -0
- package/dist/cli/npm/publish/release-lock.d.ts.map +1 -0
- package/dist/cli/npm/publish/release-lock.js +371 -0
- package/dist/cli/npm/publish/release-lock.js.map +1 -0
- package/dist/cli/npm/publish/release-phases.d.ts +93 -0
- package/dist/cli/npm/publish/release-phases.d.ts.map +1 -0
- package/dist/cli/npm/publish/release-phases.js +175 -0
- package/dist/cli/npm/publish/release-phases.js.map +1 -0
- package/dist/cli/npm/publish/release-plan.d.ts +65 -0
- package/dist/cli/npm/publish/release-plan.d.ts.map +1 -0
- package/dist/cli/npm/publish/release-plan.js +178 -0
- package/dist/cli/npm/publish/release-plan.js.map +1 -0
- package/dist/cli/npm/publish/release-preflight.job.impl.d.ts +16 -0
- package/dist/cli/npm/publish/release-preflight.job.impl.d.ts.map +1 -0
- package/dist/cli/npm/publish/release-preflight.job.impl.js +71 -0
- package/dist/cli/npm/publish/release-preflight.job.impl.js.map +1 -0
- package/dist/cli/npm/publish/release-publish.job.impl.d.ts +18 -0
- package/dist/cli/npm/publish/release-publish.job.impl.d.ts.map +1 -0
- package/dist/cli/npm/publish/release-publish.job.impl.js +295 -0
- package/dist/cli/npm/publish/release-publish.job.impl.js.map +1 -0
- package/dist/cli/npm/publish/release-workspace.d.ts +95 -0
- package/dist/cli/npm/publish/release-workspace.d.ts.map +1 -0
- package/dist/cli/npm/publish/release-workspace.js +243 -0
- package/dist/cli/npm/publish/release-workspace.js.map +1 -0
- package/dist/cli/npm/publish/sensitive-temp.d.ts +90 -0
- package/dist/cli/npm/publish/sensitive-temp.d.ts.map +1 -0
- package/dist/cli/npm/publish/sensitive-temp.js +351 -0
- package/dist/cli/npm/publish/sensitive-temp.js.map +1 -0
- package/dist/cli/npm/publish/source-snapshot.d.ts +91 -0
- package/dist/cli/npm/publish/source-snapshot.d.ts.map +1 -0
- package/dist/cli/npm/publish/source-snapshot.js +245 -0
- package/dist/cli/npm/publish/source-snapshot.js.map +1 -0
- package/dist/cli/npm/publish/types.d.ts +69 -0
- package/dist/cli/npm/publish/types.d.ts.map +1 -0
- package/dist/cli/npm/publish/types.js +2 -0
- package/dist/cli/npm/publish/types.js.map +1 -0
- package/dist/cli/npm/publish/workspace-discovery.d.ts +48 -0
- package/dist/cli/npm/publish/workspace-discovery.d.ts.map +1 -0
- package/dist/cli/npm/publish/workspace-discovery.js +313 -0
- package/dist/cli/npm/publish/workspace-discovery.js.map +1 -0
- package/dist/cli/npm/put-attested-packument.d.ts +96 -0
- package/dist/cli/npm/put-attested-packument.d.ts.map +1 -0
- package/dist/cli/npm/put-attested-packument.js +240 -0
- package/dist/cli/npm/put-attested-packument.js.map +1 -0
- package/dist/cli/npm/reconcile-latest.d.ts +55 -0
- package/dist/cli/npm/reconcile-latest.d.ts.map +1 -0
- package/dist/cli/npm/reconcile-latest.js +59 -0
- package/dist/cli/npm/reconcile-latest.js.map +1 -0
- package/dist/cli/observability/blind-spots.d.ts +10 -0
- package/dist/cli/observability/blind-spots.d.ts.map +1 -0
- package/dist/cli/observability/blind-spots.js +19 -0
- package/dist/cli/observability/blind-spots.js.map +1 -0
- package/dist/cli/observability/infra-observability-exports-list.cli-command.impl.d.ts +6 -0
- package/dist/cli/observability/infra-observability-exports-list.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/observability/infra-observability-exports-list.cli-command.impl.js +75 -0
- package/dist/cli/observability/infra-observability-exports-list.cli-command.impl.js.map +1 -0
- package/dist/cli/observability/infra-observability-set.cli-command.descriptor.d.ts +42 -21
- package/dist/cli/observability/infra-observability-set.cli-command.descriptor.d.ts.map +1 -1
- package/dist/cli/observability/infra-observability-set.cli-command.descriptor.js +3 -3
- package/dist/cli/observability/infra-observability-set.cli-command.descriptor.js.map +1 -1
- package/dist/cli/observability/infra-observability-set.cli-command.impl.d.ts +3 -2
- package/dist/cli/observability/infra-observability-set.cli-command.impl.d.ts.map +1 -1
- package/dist/cli/observability/infra-observability-set.cli-command.impl.js +14 -14
- package/dist/cli/observability/infra-observability-set.cli-command.impl.js.map +1 -1
- package/dist/cli/observability/infra-observability-status.cli-command.descriptor.d.ts +25 -17
- package/dist/cli/observability/infra-observability-status.cli-command.descriptor.d.ts.map +1 -1
- package/dist/cli/observability/infra-observability-status.cli-command.descriptor.js +1 -1
- package/dist/cli/observability/infra-observability-status.cli-command.descriptor.js.map +1 -1
- package/dist/cli/observability/infra-observability-status.cli-command.impl.d.ts +2 -1
- package/dist/cli/observability/infra-observability-status.cli-command.impl.d.ts.map +1 -1
- package/dist/cli/observability/infra-observability-status.cli-command.impl.js +30 -23
- package/dist/cli/observability/infra-observability-status.cli-command.impl.js.map +1 -1
- package/dist/cli/observability/infra-observability.cli-group.descriptor.d.ts +8 -8
- package/dist/cli/observability/infra-observability.cli-group.descriptor.js +1 -1
- package/dist/cli/observability/infra-observability.cli-group.descriptor.js.map +1 -1
- package/dist/cli/plugin.d.ts +3 -0
- package/dist/cli/plugin.d.ts.map +1 -0
- package/dist/cli/plugin.js +45 -0
- package/dist/cli/plugin.js.map +1 -0
- package/dist/cli/readiness/infra-deploy.readiness.cli-command.descriptor.d.ts +38 -0
- package/dist/cli/readiness/infra-deploy.readiness.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/readiness/infra-deploy.readiness.cli-command.descriptor.js +43 -0
- package/dist/cli/readiness/infra-deploy.readiness.cli-command.descriptor.js.map +1 -0
- package/dist/cli/readiness/infra-deploy.readiness.cli-command.impl.d.ts +117 -0
- package/dist/cli/readiness/infra-deploy.readiness.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/readiness/infra-deploy.readiness.cli-command.impl.js +678 -0
- package/dist/cli/readiness/infra-deploy.readiness.cli-command.impl.js.map +1 -0
- package/dist/cli/regenerate/infra-deploy.regenerate.cli-command.descriptor.d.ts +37 -23
- package/dist/cli/regenerate/infra-deploy.regenerate.cli-command.descriptor.d.ts.map +1 -1
- package/dist/cli/regenerate/infra-deploy.regenerate.cli-command.descriptor.js +7 -1
- package/dist/cli/regenerate/infra-deploy.regenerate.cli-command.descriptor.js.map +1 -1
- package/dist/cli/regenerate/infra-deploy.regenerate.cli-command.impl.d.ts +2 -1
- package/dist/cli/regenerate/infra-deploy.regenerate.cli-command.impl.d.ts.map +1 -1
- package/dist/cli/regenerate/infra-deploy.regenerate.cli-command.impl.js +100 -25
- package/dist/cli/regenerate/infra-deploy.regenerate.cli-command.impl.js.map +1 -1
- package/dist/cli/release/cli/latest-manifest.d.ts +148 -0
- package/dist/cli/release/cli/latest-manifest.d.ts.map +1 -0
- package/dist/cli/release/cli/latest-manifest.js +152 -0
- package/dist/cli/release/cli/latest-manifest.js.map +1 -0
- package/dist/cli/rollback/infra-deploy.rollback.cli-command.descriptor.d.ts +41 -0
- package/dist/cli/rollback/infra-deploy.rollback.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/rollback/infra-deploy.rollback.cli-command.descriptor.js +49 -0
- package/dist/cli/rollback/infra-deploy.rollback.cli-command.descriptor.js.map +1 -0
- package/dist/cli/rollback/infra-deploy.rollback.cli-command.impl.d.ts +16 -0
- package/dist/cli/rollback/infra-deploy.rollback.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/rollback/infra-deploy.rollback.cli-command.impl.js +52 -0
- package/dist/cli/rollback/infra-deploy.rollback.cli-command.impl.js.map +1 -0
- package/dist/cli/rum/infra-rum-status.cli-command.descriptor.d.ts +19 -17
- package/dist/cli/rum/infra-rum-status.cli-command.descriptor.d.ts.map +1 -1
- package/dist/cli/rum/infra-rum-status.cli-command.descriptor.js +1 -1
- package/dist/cli/rum/infra-rum-status.cli-command.descriptor.js.map +1 -1
- package/dist/cli/rum/infra-rum-status.cli-command.impl.d.ts +2 -1
- package/dist/cli/rum/infra-rum-status.cli-command.impl.d.ts.map +1 -1
- package/dist/cli/rum/infra-rum-status.cli-command.impl.js +21 -15
- package/dist/cli/rum/infra-rum-status.cli-command.impl.js.map +1 -1
- package/dist/cli/rum/infra-rum.cli-group.descriptor.d.ts +8 -8
- package/dist/cli/rum/infra-rum.cli-group.descriptor.js +1 -1
- package/dist/cli/rum/infra-rum.cli-group.descriptor.js.map +1 -1
- package/dist/cli/run/deploy-smoke.d.ts +57 -0
- package/dist/cli/run/deploy-smoke.d.ts.map +1 -0
- package/dist/cli/run/deploy-smoke.js +251 -0
- package/dist/cli/run/deploy-smoke.js.map +1 -0
- package/dist/cli/run/deployment-marker.d.ts +74 -0
- package/dist/cli/run/deployment-marker.d.ts.map +1 -0
- package/dist/cli/run/deployment-marker.js +237 -0
- package/dist/cli/run/deployment-marker.js.map +1 -0
- package/dist/cli/run/deployment-provider.d.ts +56 -0
- package/dist/cli/run/deployment-provider.d.ts.map +1 -0
- package/dist/cli/run/deployment-provider.js +295 -0
- package/dist/cli/run/deployment-provider.js.map +1 -0
- package/dist/cli/run/deployment-run-ledger.d.ts +72 -0
- package/dist/cli/run/deployment-run-ledger.d.ts.map +1 -0
- package/dist/cli/run/deployment-run-ledger.js +164 -0
- package/dist/cli/run/deployment-run-ledger.js.map +1 -0
- package/dist/cli/run/git-source-identity.d.ts +21 -0
- package/dist/cli/run/git-source-identity.d.ts.map +1 -0
- package/dist/cli/run/git-source-identity.js +73 -0
- package/dist/cli/run/git-source-identity.js.map +1 -0
- package/dist/cli/run/immutable-deployment-release.program.d.ts +403 -0
- package/dist/cli/run/immutable-deployment-release.program.d.ts.map +1 -0
- package/dist/cli/run/immutable-deployment-release.program.js +589 -0
- package/dist/cli/run/immutable-deployment-release.program.js.map +1 -0
- package/dist/cli/run/immutable-deployment-workspace.d.ts +39 -0
- package/dist/cli/run/immutable-deployment-workspace.d.ts.map +1 -0
- package/dist/cli/run/immutable-deployment-workspace.js +525 -0
- package/dist/cli/run/immutable-deployment-workspace.js.map +1 -0
- package/dist/cli/run/infra-deploy.run.cli-command.descriptor.d.ts +80 -23
- package/dist/cli/run/infra-deploy.run.cli-command.descriptor.d.ts.map +1 -1
- package/dist/cli/run/infra-deploy.run.cli-command.descriptor.js +50 -8
- package/dist/cli/run/infra-deploy.run.cli-command.descriptor.js.map +1 -1
- package/dist/cli/run/infra-deploy.run.cli-command.impl.d.ts +46 -1
- package/dist/cli/run/infra-deploy.run.cli-command.impl.d.ts.map +1 -1
- package/dist/cli/run/infra-deploy.run.cli-command.impl.js +464 -226
- package/dist/cli/run/infra-deploy.run.cli-command.impl.js.map +1 -1
- package/dist/cli/shared/build-workspace-artifacts.d.ts +147 -0
- package/dist/cli/shared/build-workspace-artifacts.d.ts.map +1 -0
- package/dist/cli/shared/build-workspace-artifacts.js +2013 -0
- package/dist/cli/shared/build-workspace-artifacts.js.map +1 -0
- package/dist/cli/shared/credential-source.d.ts +3 -0
- package/dist/cli/shared/credential-source.d.ts.map +1 -0
- package/dist/cli/shared/credential-source.js +19 -0
- package/dist/cli/shared/credential-source.js.map +1 -0
- package/dist/cli/shared/discover-deployments.d.ts +46 -5
- package/dist/cli/shared/discover-deployments.d.ts.map +1 -1
- package/dist/cli/shared/discover-deployments.js +167 -41
- package/dist/cli/shared/discover-deployments.js.map +1 -1
- package/dist/cli/shared/infer-apex-zone.d.ts +18 -0
- package/dist/cli/shared/infer-apex-zone.d.ts.map +1 -0
- package/dist/cli/shared/infer-apex-zone.js +75 -0
- package/dist/cli/shared/infer-apex-zone.js.map +1 -0
- package/dist/cli/shared/option-strings.d.ts +2 -0
- package/dist/cli/shared/option-strings.d.ts.map +1 -0
- package/dist/cli/shared/option-strings.js +13 -0
- package/dist/cli/shared/option-strings.js.map +1 -0
- package/dist/cli/shared/repository-collection.d.ts +42 -0
- package/dist/cli/shared/repository-collection.d.ts.map +1 -0
- package/dist/cli/shared/repository-collection.js +111 -0
- package/dist/cli/shared/repository-collection.js.map +1 -0
- package/dist/cli/shared/runtime-execution.d.ts +78 -0
- package/dist/cli/shared/runtime-execution.d.ts.map +1 -0
- package/dist/cli/shared/runtime-execution.js +182 -0
- package/dist/cli/shared/runtime-execution.js.map +1 -0
- package/dist/cli/shared/workspace-artifact-lock.d.ts +17 -0
- package/dist/cli/shared/workspace-artifact-lock.d.ts.map +1 -0
- package/dist/cli/shared/workspace-artifact-lock.js +37 -0
- package/dist/cli/shared/workspace-artifact-lock.js.map +1 -0
- package/dist/cli/workers/doks-worker-runtime.d.ts +238 -0
- package/dist/cli/workers/doks-worker-runtime.d.ts.map +1 -0
- package/dist/cli/workers/doks-worker-runtime.js +410 -0
- package/dist/cli/workers/doks-worker-runtime.js.map +1 -0
- package/dist/cli/workers/infra-workers-apply.cli-command.descriptor.d.ts +28 -0
- package/dist/cli/workers/infra-workers-apply.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/workers/infra-workers-apply.cli-command.descriptor.js +28 -0
- package/dist/cli/workers/infra-workers-apply.cli-command.descriptor.js.map +1 -0
- package/dist/cli/workers/infra-workers-apply.cli-command.impl.d.ts +6 -0
- package/dist/cli/workers/infra-workers-apply.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/workers/infra-workers-apply.cli-command.impl.js +75 -0
- package/dist/cli/workers/infra-workers-apply.cli-command.impl.js.map +1 -0
- package/dist/cli/workers/infra-workers-build.cli-command.descriptor.d.ts +31 -0
- package/dist/cli/workers/infra-workers-build.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/workers/infra-workers-build.cli-command.descriptor.js +32 -0
- package/dist/cli/workers/infra-workers-build.cli-command.descriptor.js.map +1 -0
- package/dist/cli/workers/infra-workers-build.cli-command.impl.d.ts +6 -0
- package/dist/cli/workers/infra-workers-build.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/workers/infra-workers-build.cli-command.impl.js +67 -0
- package/dist/cli/workers/infra-workers-build.cli-command.impl.js.map +1 -0
- package/dist/cli/workers/infra-workers-logs.cli-command.descriptor.d.ts +48 -0
- package/dist/cli/workers/infra-workers-logs.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/workers/infra-workers-logs.cli-command.descriptor.js +60 -0
- package/dist/cli/workers/infra-workers-logs.cli-command.descriptor.js.map +1 -0
- package/dist/cli/workers/infra-workers-logs.cli-command.impl.d.ts +6 -0
- package/dist/cli/workers/infra-workers-logs.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/workers/infra-workers-logs.cli-command.impl.js +74 -0
- package/dist/cli/workers/infra-workers-logs.cli-command.impl.js.map +1 -0
- package/dist/cli/workers/infra-workers.cli-group.descriptor.d.ts +12 -0
- package/dist/cli/workers/infra-workers.cli-group.descriptor.d.ts.map +1 -0
- package/dist/cli/workers/infra-workers.cli-group.descriptor.js +12 -0
- package/dist/cli/workers/infra-workers.cli-group.descriptor.js.map +1 -0
- package/dist/config-loader.d.ts +60 -13
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/config-loader.js +371 -104
- package/dist/config-loader.js.map +1 -1
- package/dist/declared-provider.d.ts +27 -0
- package/dist/declared-provider.d.ts.map +1 -0
- package/dist/declared-provider.js +46 -0
- package/dist/declared-provider.js.map +1 -0
- package/dist/deployment-collisions.d.ts +55 -0
- package/dist/deployment-collisions.d.ts.map +1 -0
- package/dist/deployment-collisions.js +70 -0
- package/dist/deployment-collisions.js.map +1 -0
- package/dist/deployment-environment.d.ts +57 -0
- package/dist/deployment-environment.d.ts.map +1 -0
- package/dist/deployment-environment.js +198 -0
- package/dist/deployment-environment.js.map +1 -0
- package/dist/deployment-settings.d.ts +27 -0
- package/dist/deployment-settings.d.ts.map +1 -0
- package/dist/deployment-settings.js +52 -0
- package/dist/deployment-settings.js.map +1 -0
- package/dist/deployment.config.descriptor.d.ts +59 -0
- package/dist/deployment.config.descriptor.d.ts.map +1 -0
- package/dist/deployment.config.descriptor.js +54 -0
- package/dist/deployment.config.descriptor.js.map +1 -0
- package/dist/descriptors/observability/infra-observability-exports-list.cli-command.descriptor.d.ts +29 -0
- package/dist/descriptors/observability/infra-observability-exports-list.cli-command.descriptor.d.ts.map +1 -0
- package/dist/descriptors/observability/infra-observability-exports-list.cli-command.descriptor.js +35 -0
- package/dist/descriptors/observability/infra-observability-exports-list.cli-command.descriptor.js.map +1 -0
- package/dist/descriptors/observability/infra-observability-exports.cli-group.descriptor.d.ts +12 -0
- package/dist/descriptors/observability/infra-observability-exports.cli-group.descriptor.d.ts.map +1 -0
- package/dist/descriptors/observability/infra-observability-exports.cli-group.descriptor.js +12 -0
- package/dist/descriptors/observability/infra-observability-exports.cli-group.descriptor.js.map +1 -0
- package/dist/index.d.ts +11 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/infra-deploy.plugin.d.ts +2 -2
- package/dist/infra-deploy.plugin.d.ts.map +1 -1
- package/dist/infra-deploy.plugin.js +63 -20
- package/dist/infra-deploy.plugin.js.map +1 -1
- package/dist/logger.d.ts +10 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +11 -0
- package/dist/logger.js.map +1 -0
- package/dist/migrations/migrate-workspace.d.ts +21 -0
- package/dist/migrations/migrate-workspace.d.ts.map +1 -0
- package/dist/migrations/migrate-workspace.js +83 -0
- package/dist/migrations/migrate-workspace.js.map +1 -0
- package/dist/migrations/run-migrate.d.ts +15 -0
- package/dist/migrations/run-migrate.d.ts.map +1 -0
- package/dist/migrations/run-migrate.js +314 -0
- package/dist/migrations/run-migrate.js.map +1 -0
- package/dist/regenerate.d.ts +52 -30
- package/dist/regenerate.d.ts.map +1 -1
- package/dist/regenerate.js +176 -148
- package/dist/regenerate.js.map +1 -1
- package/dist/runtime-execution.d.ts +5 -0
- package/dist/runtime-execution.d.ts.map +1 -0
- package/dist/runtime-execution.js +12 -0
- package/dist/runtime-execution.js.map +1 -0
- package/dist/schemas/migrate.d.ts +110 -0
- package/dist/schemas/migrate.d.ts.map +1 -0
- package/dist/schemas/migrate.js +55 -0
- package/dist/schemas/migrate.js.map +1 -0
- package/dist/schemas/proof-registry.d.ts +25 -0
- package/dist/schemas/proof-registry.d.ts.map +1 -0
- package/dist/schemas/proof-registry.js +24 -0
- package/dist/schemas/proof-registry.js.map +1 -0
- package/dist/schemas/release-grants.d.ts +7 -0
- package/dist/schemas/release-grants.d.ts.map +1 -0
- package/dist/schemas/release-grants.js +6 -0
- package/dist/schemas/release-grants.js.map +1 -0
- package/dist/schemas/release-jobs.d.ts +444 -0
- package/dist/schemas/release-jobs.d.ts.map +1 -0
- package/dist/schemas/release-jobs.js +204 -0
- package/dist/schemas/release-jobs.js.map +1 -0
- package/dist/schemas/service-release.d.ts +15 -0
- package/dist/schemas/service-release.d.ts.map +1 -0
- package/dist/schemas/service-release.js +13 -0
- package/dist/schemas/service-release.js.map +1 -0
- package/dist/schemas/worker-logs.d.ts +42 -0
- package/dist/schemas/worker-logs.d.ts.map +1 -0
- package/dist/schemas/worker-logs.js +29 -0
- package/dist/schemas/worker-logs.js.map +1 -0
- package/dist/storage/deployment-runs.schema.d.ts +199 -0
- package/dist/storage/deployment-runs.schema.d.ts.map +1 -0
- package/dist/storage/deployment-runs.schema.js +61 -0
- package/dist/storage/deployment-runs.schema.js.map +1 -0
- package/dist/storage/deployment-runs.storage.descriptor.d.ts +4 -0
- package/dist/storage/deployment-runs.storage.descriptor.d.ts.map +1 -0
- package/dist/storage/deployment-runs.storage.descriptor.js +36 -0
- package/dist/storage/deployment-runs.storage.descriptor.js.map +1 -0
- package/package.json +110 -42
- package/SPEC.md +0 -181
- package/src/cli/alerts/infra-alerts-create.cli-command.descriptor.ts +0 -61
- package/src/cli/alerts/infra-alerts-create.cli-command.impl.ts +0 -131
- package/src/cli/alerts/infra-alerts-delete.cli-command.descriptor.ts +0 -36
- package/src/cli/alerts/infra-alerts-delete.cli-command.impl.ts +0 -75
- package/src/cli/alerts/infra-alerts-list.cli-command.descriptor.ts +0 -27
- package/src/cli/alerts/infra-alerts-list.cli-command.impl.ts +0 -88
- package/src/cli/alerts/infra-alerts.cli-group.descriptor.ts +0 -12
- package/src/cli/audit/infra-audit.cli-command.descriptor.ts +0 -28
- package/src/cli/audit/infra-audit.cli-command.impl.ts +0 -293
- package/src/cli/infra-deploy.cli-group.descriptor.ts +0 -12
- package/src/cli/infra.cli-group.descriptor.ts +0 -11
- package/src/cli/list/infra-deploy.list.cli-command.descriptor.ts +0 -29
- package/src/cli/list/infra-deploy.list.cli-command.impl.ts +0 -125
- package/src/cli/logs/infra-logs.cli-command.descriptor.ts +0 -65
- package/src/cli/logs/infra-logs.cli-command.impl.ts +0 -354
- package/src/cli/logs/stream-worker-tail.ts +0 -202
- package/src/cli/npm/infra-npm-publish.cli-command.descriptor.ts +0 -75
- package/src/cli/npm/infra-npm-publish.cli-command.impl.ts +0 -474
- package/src/cli/npm/infra-npm.cli-group.descriptor.ts +0 -12
- package/src/cli/observability/infra-observability-set.cli-command.descriptor.ts +0 -57
- package/src/cli/observability/infra-observability-set.cli-command.impl.ts +0 -173
- package/src/cli/observability/infra-observability-status.cli-command.descriptor.ts +0 -35
- package/src/cli/observability/infra-observability-status.cli-command.impl.ts +0 -124
- package/src/cli/observability/infra-observability.cli-group.descriptor.ts +0 -12
- package/src/cli/regenerate/infra-deploy.regenerate.cli-command.descriptor.ts +0 -36
- package/src/cli/regenerate/infra-deploy.regenerate.cli-command.impl.ts +0 -103
- package/src/cli/rum/infra-rum-status.cli-command.descriptor.ts +0 -27
- package/src/cli/rum/infra-rum-status.cli-command.impl.ts +0 -112
- package/src/cli/rum/infra-rum.cli-group.descriptor.ts +0 -12
- package/src/cli/run/infra-deploy.run.cli-command.descriptor.ts +0 -49
- package/src/cli/run/infra-deploy.run.cli-command.impl.ts +0 -330
- package/src/cli/shared/discover-deployments.ts +0 -127
- package/src/config-loader.ts +0 -179
- package/src/index.ts +0 -11
- package/src/infra-deploy.plugin.ts +0 -83
- package/src/regenerate.ts +0 -230
|
@@ -0,0 +1,2013 @@
|
|
|
1
|
+
import { satisfies, validRange } from 'semver';
|
|
2
|
+
import * as z from 'zod/v4';
|
|
3
|
+
import { CapabilityError, settle } from '@vibesdotdev/kernel';
|
|
4
|
+
import { JsonValueSchema } from '@vibesdotdev/kernel/schemas/json';
|
|
5
|
+
import { planDag } from '@vibesdotdev/shared/schemas';
|
|
6
|
+
import { resolveHash } from '@vibesdotdev/crypto';
|
|
7
|
+
import { DeploymentBuildEvidenceSchema } from '@vibesdotdev/infra-core/kinds';
|
|
8
|
+
import { realpathChecked, resolveFileSystemAdapter } from '@vibesdotdev/filesystem';
|
|
9
|
+
import { getRuntimePath } from '@vibesdotdev/paths';
|
|
10
|
+
import { declaredPackageDirectories } from "../npm/publish/workspace-discovery.js";
|
|
11
|
+
import { parsePublisherManifest } from "../npm/publish/publisher-manifest.js";
|
|
12
|
+
import { infraDeployLogger } from "../../logger.js";
|
|
13
|
+
import { withWorkspaceArtifactLock } from "./workspace-artifact-lock.js";
|
|
14
|
+
import { runRuntimeCommand, registryTokenEnv } from "./runtime-execution.js";
|
|
15
|
+
function failureMessage(failure) {
|
|
16
|
+
return failure instanceof Error ? failure.message : failure.description;
|
|
17
|
+
}
|
|
18
|
+
/** The separator `paths.join` actually used, so containment checks follow the host. */
|
|
19
|
+
function pathSeparator(paths) {
|
|
20
|
+
const joined = paths.join('a', 'b');
|
|
21
|
+
const separator = joined.slice(1, joined.length - 1);
|
|
22
|
+
if (separator.length !== 1) {
|
|
23
|
+
throw new CapabilityError('The path host did not join two segments with one separator.', 'UNAVAILABLE', { reason: 'path-separator-unavailable' });
|
|
24
|
+
}
|
|
25
|
+
return separator;
|
|
26
|
+
}
|
|
27
|
+
async function resolveArtifactHost(runtime) {
|
|
28
|
+
const [files, paths, hash] = await Promise.all([
|
|
29
|
+
resolveFileSystemAdapter(runtime, { binding: false }),
|
|
30
|
+
getRuntimePath(runtime),
|
|
31
|
+
resolveHash(runtime)
|
|
32
|
+
]);
|
|
33
|
+
return {
|
|
34
|
+
runtime,
|
|
35
|
+
files,
|
|
36
|
+
paths,
|
|
37
|
+
sep: pathSeparator(paths),
|
|
38
|
+
sha256: () => hash.createSha256()
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async function readText(files, path) {
|
|
42
|
+
const contents = await files.readFile(path, 'utf8');
|
|
43
|
+
return contents instanceof Uint8Array ? new TextDecoder().decode(contents) : contents;
|
|
44
|
+
}
|
|
45
|
+
async function readBytes(files, path) {
|
|
46
|
+
const contents = await files.readFile(path);
|
|
47
|
+
return contents instanceof Uint8Array ? contents : new TextEncoder().encode(contents);
|
|
48
|
+
}
|
|
49
|
+
/** Follows one symlink, matching host `stat`. A link is not a directory entry. */
|
|
50
|
+
async function followedStat(host, path) {
|
|
51
|
+
const listed = await host.files.stat(path);
|
|
52
|
+
if (listed.kind !== 'symlink')
|
|
53
|
+
return listed;
|
|
54
|
+
return host.files.stat(await realpathChecked(host.files, path));
|
|
55
|
+
}
|
|
56
|
+
async function removeIfPresent(files, path) {
|
|
57
|
+
const removed = await settle(async () => {
|
|
58
|
+
await files.remove(path);
|
|
59
|
+
});
|
|
60
|
+
if (removed.ok || isMissingPathError(removed.cause))
|
|
61
|
+
return;
|
|
62
|
+
throw removed.cause;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The build-artifact directory a package's manifest declares via
|
|
66
|
+
* `"vibes": { "artifactsDir": "…" }`, or `dist` when it declares nothing.
|
|
67
|
+
* Every package that does not opt in keeps the original `dist`-only
|
|
68
|
+
* behaviour untouched.
|
|
69
|
+
*/
|
|
70
|
+
function resolveArtifactsDir(pkg) {
|
|
71
|
+
return pkg.packageJson.vibes?.artifactsDir ?? 'dist';
|
|
72
|
+
}
|
|
73
|
+
/** Source admission and emitted bytes are separate receipts. A source hash never certifies output. */
|
|
74
|
+
export const WorkspaceBuildArtifactReceiptSchema = DeploymentBuildEvidenceSchema.shape.artifact.unwrap().extend({
|
|
75
|
+
files: z.array(z.object({ path: z.string(), digest: z.string().regex(/^[a-f0-9]{64}$/) }))
|
|
76
|
+
});
|
|
77
|
+
/** Installed locks are build inputs while a provider prepares its final artifacts. Missing locks participate too. */
|
|
78
|
+
export async function workspaceBuildLockDigest(input) {
|
|
79
|
+
const host = await resolveArtifactHost(input.runtime);
|
|
80
|
+
const root = await realpathChecked(host.files, input.workspaceRoot);
|
|
81
|
+
const locks = [];
|
|
82
|
+
for (const lock of [...new Set(input.lockFiles)].sort()) {
|
|
83
|
+
const path = host.paths.resolve(root, lock);
|
|
84
|
+
if (!isPathWithin(host, root, path))
|
|
85
|
+
throw new CapabilityError('An installed lock escapes its owned workspace.', 'INVALID_INPUT', { reason: 'workspace-artifact-outside' });
|
|
86
|
+
const stated = await settle(() => host.files.stat(path));
|
|
87
|
+
if (!stated.ok) {
|
|
88
|
+
if (!isMissingPathError(stated.cause))
|
|
89
|
+
throw stated.cause;
|
|
90
|
+
locks.push({ path: lock, digest: null });
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (stated.value.kind !== 'file' || !isPathWithin(host, root, await realpathChecked(host.files, path))) {
|
|
94
|
+
throw new CapabilityError('An installed lock is not an owned regular file.', 'INVALID_INPUT', { reason: 'workspace-artifact-invalid' });
|
|
95
|
+
}
|
|
96
|
+
locks.push({ path: lock, digest: host.sha256().update(await readBytes(host.files, path)).digestHex() });
|
|
97
|
+
}
|
|
98
|
+
return host.sha256().update(JSON.stringify(locks)).digestHex();
|
|
99
|
+
}
|
|
100
|
+
/** Hash the actual run-owned outputs and installed locks, including additions and same-size edits. */
|
|
101
|
+
export async function sealWorkspaceBuildArtifacts(input) {
|
|
102
|
+
const host = await resolveArtifactHost(input.runtime);
|
|
103
|
+
const root = await realpathChecked(host.files, input.workspaceRoot);
|
|
104
|
+
const files = new Map();
|
|
105
|
+
const visit = async (path) => {
|
|
106
|
+
if (!isPathWithin(host, root, path)) {
|
|
107
|
+
throw new CapabilityError('A build artifact escapes its owned workspace.', 'INVALID_INPUT', { reason: 'workspace-artifact-outside' });
|
|
108
|
+
}
|
|
109
|
+
const stated = await settle(() => host.files.stat(path));
|
|
110
|
+
if (!stated.ok) {
|
|
111
|
+
if (isMissingPathError(stated.cause))
|
|
112
|
+
throw new CapabilityError('A build artifact disappeared before consumption.', 'CONFLICT', { reason: 'workspace-artifact-missing', cause: stated.cause });
|
|
113
|
+
throw stated.cause;
|
|
114
|
+
}
|
|
115
|
+
const entry = stated.value;
|
|
116
|
+
if (entry.kind === 'symlink') {
|
|
117
|
+
throw new CapabilityError('Build artifact receipts require owned regular files.', 'INVALID_INPUT', { reason: 'workspace-artifact-symlink' });
|
|
118
|
+
}
|
|
119
|
+
if (!isPathWithin(host, root, await realpathChecked(host.files, path))) {
|
|
120
|
+
throw new CapabilityError('A build artifact resolves outside its owned workspace.', 'INVALID_INPUT', { reason: 'workspace-artifact-outside' });
|
|
121
|
+
}
|
|
122
|
+
if (entry.kind === 'directory') {
|
|
123
|
+
for (const child of await host.files.list(path))
|
|
124
|
+
await visit(host.paths.join(path, child.name));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (entry.kind !== 'file') {
|
|
128
|
+
throw new CapabilityError('Build output is not a regular file.', 'INVALID_INPUT', { reason: 'workspace-artifact-invalid' });
|
|
129
|
+
}
|
|
130
|
+
files.set(toPosixPath(host, host.paths.relative(root, path)), host.sha256().update(await readBytes(host.files, path)).digestHex());
|
|
131
|
+
};
|
|
132
|
+
for (const directory of input.directories)
|
|
133
|
+
await visit(host.paths.resolve(root, directory));
|
|
134
|
+
if (files.size === 0) {
|
|
135
|
+
throw new CapabilityError('The build emitted no certifiable artifact bytes.', 'CONFLICT', { reason: 'workspace-artifact-empty' });
|
|
136
|
+
}
|
|
137
|
+
for (const lock of input.lockFiles) {
|
|
138
|
+
const path = host.paths.resolve(root, lock);
|
|
139
|
+
if (await host.files.exists(path))
|
|
140
|
+
await visit(path);
|
|
141
|
+
}
|
|
142
|
+
const recorded = [...files].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([path, digest]) => ({ path, digest }));
|
|
143
|
+
const references = [...new Set(input.references ?? [])].sort();
|
|
144
|
+
const artifactDigest = host.sha256().update(JSON.stringify(references.length ? { files: recorded, references } : recorded)).digestHex();
|
|
145
|
+
return WorkspaceBuildArtifactReceiptSchema.parse({ compositionDigest: input.compositionDigest, sourceCommit: input.sourceCommit, artifactDigest, files: recorded, references });
|
|
146
|
+
}
|
|
147
|
+
/** Re-read the bytes at the provider boundary; no mtime, size, or source-stamp shortcut. */
|
|
148
|
+
export async function assertWorkspaceBuildArtifactReceipt(input, receipt) {
|
|
149
|
+
const admitted = WorkspaceBuildArtifactReceiptSchema.parse(receipt);
|
|
150
|
+
if (admitted.compositionDigest !== input.compositionDigest || admitted.sourceCommit !== input.sourceCommit) {
|
|
151
|
+
throw new CapabilityError('Build output belongs to another source or composition.', 'CONFLICT', { reason: 'workspace-artifact-identity-mismatch' });
|
|
152
|
+
}
|
|
153
|
+
const current = await sealWorkspaceBuildArtifacts(input);
|
|
154
|
+
if (current.artifactDigest !== admitted.artifactDigest || JSON.stringify(current.references) !== JSON.stringify([...new Set(admitted.references ?? [])].sort())) {
|
|
155
|
+
throw new CapabilityError('Build output changed before provider consumption.', 'CONFLICT', { reason: 'workspace-artifact-digest-mismatch' });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const CSS_IMPORT_RE = /@import\s+['"](@vibesdotdev\/[a-zA-Z0-9_./-]+)['"]/g;
|
|
159
|
+
const SOURCE_EXTENSIONS = new Set([
|
|
160
|
+
'.css',
|
|
161
|
+
'.cjs',
|
|
162
|
+
'.js',
|
|
163
|
+
'.json',
|
|
164
|
+
'.mjs',
|
|
165
|
+
'.svelte',
|
|
166
|
+
'.ts',
|
|
167
|
+
'.tsx'
|
|
168
|
+
]);
|
|
169
|
+
const SKIP_DIRS = new Set([
|
|
170
|
+
'.git',
|
|
171
|
+
'.svelte-kit',
|
|
172
|
+
'.turbo',
|
|
173
|
+
'__stubs__',
|
|
174
|
+
'__tests__',
|
|
175
|
+
'coverage',
|
|
176
|
+
'dist',
|
|
177
|
+
'node_modules',
|
|
178
|
+
'test-results'
|
|
179
|
+
]);
|
|
180
|
+
const WORKSPACE_DIAGNOSTIC_MAX_LINES = 40;
|
|
181
|
+
const WORKSPACE_DIAGNOSTIC_MAX_CHARS = 4000;
|
|
182
|
+
const WORKSPACE_DIAGNOSTIC_MAX_LINE_CHARS = 1000;
|
|
183
|
+
/** The ESC byte of an ANSI color sequence, built from its code point so the
|
|
184
|
+
* pattern carries no literal control byte (the sequence itself is the target). */
|
|
185
|
+
const ANSI_ESCAPE_START = String.fromCodePoint(27);
|
|
186
|
+
const ANSI_ESCAPE_RE = new RegExp(`${ANSI_ESCAPE_START}\\[[0-9;]*m`, "g");
|
|
187
|
+
const BEARER_RE = /\bBearer\s+[^\s,;]+/gi;
|
|
188
|
+
const SENSITIVE_ASSIGNMENT_RE = /\b([A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|CREDENTIAL|API_?KEY|PRIVATE_?KEY)[A-Z0-9_]*)\b\s*[:=]\s*["']?[^"',\s]+["']?/gi;
|
|
189
|
+
const SENSITIVE_QUERY_RE = /([?&](?:token|secret|password|api[_-]?key)=)[^&\s]+/gi;
|
|
190
|
+
const URL_PASSWORD_RE = /(https?:\/\/[^:\s/@]+:)[^@\s/]+@/gi;
|
|
191
|
+
/** True for the C0 control characters the sanitizer strips (tab, newline and
|
|
192
|
+
* carriage return are preserved as compiler output formatting). */
|
|
193
|
+
function isStrippedControlChar(code) {
|
|
194
|
+
return (code <= 31 && code !== 9 && code !== 10 && code !== 13) || code === 127;
|
|
195
|
+
}
|
|
196
|
+
export function sanitizeWorkspaceBuildDiagnostic(value) {
|
|
197
|
+
const stripped = Array.from(value)
|
|
198
|
+
.filter((ch) => !isStrippedControlChar(ch.codePointAt(0) ?? 0))
|
|
199
|
+
.join('');
|
|
200
|
+
return stripped
|
|
201
|
+
.replace(ANSI_ESCAPE_RE, '')
|
|
202
|
+
.replace(BEARER_RE, 'Bearer [redacted]')
|
|
203
|
+
.replace(SENSITIVE_ASSIGNMENT_RE, '$1=[redacted]')
|
|
204
|
+
.replace(SENSITIVE_QUERY_RE, '$1[redacted]')
|
|
205
|
+
.replace(URL_PASSWORD_RE, '$1[redacted]@');
|
|
206
|
+
}
|
|
207
|
+
function createWorkspaceDiagnosticTail() {
|
|
208
|
+
const entries = [];
|
|
209
|
+
let omitted = 0;
|
|
210
|
+
const renderedLength = () => entries.reduce((total, entry) => total + entry.line.length + entry.stream.length + 5, 0);
|
|
211
|
+
return {
|
|
212
|
+
push(stream, line) {
|
|
213
|
+
const sanitized = sanitizeWorkspaceBuildDiagnostic(line);
|
|
214
|
+
const bounded = sanitized.length <= WORKSPACE_DIAGNOSTIC_MAX_LINE_CHARS
|
|
215
|
+
? sanitized
|
|
216
|
+
: `${sanitized.slice(0, WORKSPACE_DIAGNOSTIC_MAX_LINE_CHARS)}… [line truncated]`;
|
|
217
|
+
entries.push({ stream, line: bounded });
|
|
218
|
+
while (entries.length > WORKSPACE_DIAGNOSTIC_MAX_LINES ||
|
|
219
|
+
renderedLength() > WORKSPACE_DIAGNOSTIC_MAX_CHARS) {
|
|
220
|
+
entries.shift();
|
|
221
|
+
omitted += 1;
|
|
222
|
+
}
|
|
223
|
+
return bounded;
|
|
224
|
+
},
|
|
225
|
+
render() {
|
|
226
|
+
if (entries.length === 0)
|
|
227
|
+
return '[no compiler output captured]';
|
|
228
|
+
const prefix = omitted > 0 ? `[${omitted} earlier diagnostic line(s) truncated]\n` : '';
|
|
229
|
+
return `${prefix}${entries.map((entry) => `[${entry.stream}] ${entry.line}`).join('\n')}`;
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* App hosts ("app") and other never-published roots ("never-publish") are not
|
|
235
|
+
* package tiers, whatever their `private` flag says. Publish discovery skips
|
|
236
|
+
* them the same way; parsing them as publisher manifests refuses every deploy
|
|
237
|
+
* in a workspace that contains one (for example vessel-desktop, `private: false`).
|
|
238
|
+
*/
|
|
239
|
+
const PrivateAppMemberSchema = z
|
|
240
|
+
.object({ vibes: z.object({ visibility: z.enum(['app', 'never-publish']) }).loose() })
|
|
241
|
+
.loose();
|
|
242
|
+
async function readJson(host, path) {
|
|
243
|
+
const read = await settle(async () => JSON.parse(await readText(host.files, path)));
|
|
244
|
+
if (!read.ok) {
|
|
245
|
+
// A missing manifest is a declared-absent input; any other read failure is real.
|
|
246
|
+
if (isMissingPathError(read.cause))
|
|
247
|
+
return undefined;
|
|
248
|
+
throw read.cause;
|
|
249
|
+
}
|
|
250
|
+
if (PrivateAppMemberSchema.safeParse(read.value).success)
|
|
251
|
+
return undefined;
|
|
252
|
+
return parsePublisherManifest(read.value);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* What the deployed app's own manifest is read for: the workspace packages it
|
|
256
|
+
* declares. An app is never a publish manifest (`vibes.visibility: "app"`).
|
|
257
|
+
*/
|
|
258
|
+
const AppDependencyManifestSchema = z.looseObject({
|
|
259
|
+
dependencies: z.record(z.string(), z.string()).optional(),
|
|
260
|
+
peerDependencies: z.record(z.string(), z.string()).optional(),
|
|
261
|
+
peerDependenciesMeta: z
|
|
262
|
+
.record(z.string(), z.object({ optional: z.boolean().optional() }).catchall(JsonValueSchema))
|
|
263
|
+
.optional()
|
|
264
|
+
});
|
|
265
|
+
async function readAppManifest(host, path) {
|
|
266
|
+
const read = await settle(async () => AppDependencyManifestSchema.parse(JSON.parse(await readText(host.files, path))));
|
|
267
|
+
if (read.ok)
|
|
268
|
+
return read.value;
|
|
269
|
+
// A missing manifest is a declared-absent input; any other read failure is real.
|
|
270
|
+
if (isMissingPathError(read.cause))
|
|
271
|
+
return undefined;
|
|
272
|
+
throw read.cause;
|
|
273
|
+
}
|
|
274
|
+
/** The errno facts a raw host absence carries. */
|
|
275
|
+
const ErrnoCodeSchema = z.object({ code: z.string().optional() }).loose();
|
|
276
|
+
function isMissingPathError(error) {
|
|
277
|
+
if (error instanceof CapabilityError && error.code === 'NOT_FOUND')
|
|
278
|
+
return true;
|
|
279
|
+
const facts = ErrnoCodeSchema.safeParse(error);
|
|
280
|
+
return facts.success && (facts.data.code === 'ENOENT' || facts.data.code === 'ENOTDIR');
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* A deploy root's declared `workspaces` is a string array or `{ packages: string[] }`.
|
|
284
|
+
* The kernel's listing reads any other shape as "no members" (kernel
|
|
285
|
+
* bootstrap/discovery.ts WorkspacesManifestSchema), so the deploy refuses it here
|
|
286
|
+
* rather than planning an empty closure.
|
|
287
|
+
*/
|
|
288
|
+
const DeployRootWorkspacesSchema = z.looseObject({
|
|
289
|
+
workspaces: z.union([z.array(z.string()), z.looseObject({ packages: z.array(z.string()) })]).optional()
|
|
290
|
+
});
|
|
291
|
+
async function discoverWorkspacePackages(host, workspaceRoot) {
|
|
292
|
+
const packages = new Map();
|
|
293
|
+
const canonicalRoot = await realpathChecked(host.files, workspaceRoot);
|
|
294
|
+
const directories = new Set();
|
|
295
|
+
const rootManifest = await settle(async () => DeployRootWorkspacesSchema.parse(JSON.parse(await readText(host.files, host.paths.join(workspaceRoot, 'package.json')))));
|
|
296
|
+
if (!rootManifest.ok) {
|
|
297
|
+
throw new Error('Workspace root package.json must declare a string workspaces array');
|
|
298
|
+
}
|
|
299
|
+
// The kernel owns the workspace dialect (braces, `!` exclusions, `vibes.packageRoots`)
|
|
300
|
+
// and lists only declared members that exist, so a deploy from a partial checkout
|
|
301
|
+
// never fails on a declared path it does not hold.
|
|
302
|
+
for (const directory of await declaredPackageDirectories(host.runtime, workspaceRoot, undefined)) {
|
|
303
|
+
const canonicalDirectory = await realpathChecked(host.files, directory);
|
|
304
|
+
if (canonicalDirectory !== canonicalRoot &&
|
|
305
|
+
!canonicalDirectory.startsWith(`${canonicalRoot}${host.sep}`)) {
|
|
306
|
+
throw new Error(`Workspace package escapes the workspace root: ${directory}`);
|
|
307
|
+
}
|
|
308
|
+
// Overlapping declarations identify one member, not two.
|
|
309
|
+
if (directories.has(canonicalDirectory))
|
|
310
|
+
continue;
|
|
311
|
+
// A directory that matches a workspace glob but holds no manifest is not a
|
|
312
|
+
// workspace member — the package manager resolves membership the same way, so
|
|
313
|
+
// treating it as fatal makes the deploy disagree with the install that produced
|
|
314
|
+
// node_modules. It also makes the outcome depend on untracked local state: a
|
|
315
|
+
// folded package leaves `dist/` and `node_modules/` behind at its old path, and
|
|
316
|
+
// that residue alone would fail every deploy from an otherwise clean checkout.
|
|
317
|
+
// A manifest that exists but does not parse is still fatal below.
|
|
318
|
+
const manifestPath = host.paths.join(canonicalDirectory, 'package.json');
|
|
319
|
+
if (!(await host.files.exists(manifestPath))) {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
directories.add(canonicalDirectory);
|
|
323
|
+
const rawManifestOutcome = await settle(async () => JSON.parse(await readText(host.files, manifestPath)));
|
|
324
|
+
if (!rawManifestOutcome.ok) {
|
|
325
|
+
throw new Error(`Invalid workspace package manifest at ${host.paths.relative(workspaceRoot, canonicalDirectory)}: ${failureMessage(rawManifestOutcome.cause)}`);
|
|
326
|
+
}
|
|
327
|
+
if (PrivateAppMemberSchema.safeParse(rawManifestOutcome.value).success) {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
const manifestOutcome = await settle(async () => parsePublisherManifest(rawManifestOutcome.value));
|
|
331
|
+
if (!manifestOutcome.ok) {
|
|
332
|
+
throw new Error(`Invalid workspace package manifest at ${host.paths.relative(workspaceRoot, canonicalDirectory)}: ${failureMessage(manifestOutcome.cause)}`);
|
|
333
|
+
}
|
|
334
|
+
const packageJson = manifestOutcome.value;
|
|
335
|
+
if (!packageJson.name) {
|
|
336
|
+
throw new Error(`Workspace package manifest has no name: ${host.paths.relative(workspaceRoot, canonicalDirectory)}`);
|
|
337
|
+
}
|
|
338
|
+
if (packages.has(packageJson.name)) {
|
|
339
|
+
throw new Error(`Duplicate workspace package name: ${packageJson.name}`);
|
|
340
|
+
}
|
|
341
|
+
packages.set(packageJson.name, {
|
|
342
|
+
name: packageJson.name,
|
|
343
|
+
dir: canonicalDirectory,
|
|
344
|
+
packageJson
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
return packages;
|
|
348
|
+
}
|
|
349
|
+
async function resolveWorkspacePackages(host, workspaceRoot, packageScope) {
|
|
350
|
+
if (!packageScope)
|
|
351
|
+
return discoverWorkspacePackages(host, workspaceRoot);
|
|
352
|
+
const canonicalRoot = await realpathChecked(host.files, workspaceRoot);
|
|
353
|
+
const directories = new Set();
|
|
354
|
+
const scoped = new Map();
|
|
355
|
+
for (const [name, pkg] of packageScope) {
|
|
356
|
+
if (name !== pkg.name || (pkg.packageJson.name && pkg.packageJson.name !== name)) {
|
|
357
|
+
throw new Error(`Workspace package scope identity mismatch for ${name}`);
|
|
358
|
+
}
|
|
359
|
+
const canonicalDirectory = await realpathChecked(host.files, pkg.dir);
|
|
360
|
+
if (canonicalDirectory !== canonicalRoot &&
|
|
361
|
+
!canonicalDirectory.startsWith(`${canonicalRoot}${host.sep}`)) {
|
|
362
|
+
throw new Error(`Workspace package scope escapes the workspace root: ${pkg.dir}`);
|
|
363
|
+
}
|
|
364
|
+
if (directories.has(canonicalDirectory)) {
|
|
365
|
+
throw new Error(`Workspace package scope repeats directory ${canonicalDirectory}`);
|
|
366
|
+
}
|
|
367
|
+
directories.add(canonicalDirectory);
|
|
368
|
+
scoped.set(name, { ...pkg, dir: canonicalDirectory });
|
|
369
|
+
}
|
|
370
|
+
return scoped;
|
|
371
|
+
}
|
|
372
|
+
function hasBuildArtifacts(pkg) {
|
|
373
|
+
if (!pkg.packageJson.scripts?.build)
|
|
374
|
+
return false;
|
|
375
|
+
if (pkg.packageJson.vibes?.artifactsDir !== undefined)
|
|
376
|
+
return true;
|
|
377
|
+
return JSON.stringify({
|
|
378
|
+
main: pkg.packageJson.main,
|
|
379
|
+
types: pkg.packageJson.types,
|
|
380
|
+
svelte: pkg.packageJson.svelte,
|
|
381
|
+
exports: pkg.packageJson.exports
|
|
382
|
+
}).includes(resolveArtifactsDir(pkg));
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* `build` is a consumer-facing convenience script, not a contract that it
|
|
386
|
+
* builds ONLY the publishable artifact. `kit` (and other Svelte packages that
|
|
387
|
+
* ship both a library and a local preview/demo app) declare `build: "bun run
|
|
388
|
+
* package && bun run build:app"` — `build:app` is a full Vite/SvelteKit
|
|
389
|
+
* compile of the preview app, something no consumer resolving `kit`'s dist,
|
|
390
|
+
* and no publish of `kit` to the registry, ever needs. The `package` script
|
|
391
|
+
* (the `svelte-package`/`prepack` convention: producing exactly the files
|
|
392
|
+
* `main`/`exports`/`files` ship) is what a dependency-artifact build wants
|
|
393
|
+
* whenever a package declares one; `build` stays the fallback for every
|
|
394
|
+
* package that has not split the two.
|
|
395
|
+
*/
|
|
396
|
+
function resolveBuildScriptName(pkg) {
|
|
397
|
+
return pkg.packageJson.scripts?.package ? 'package' : 'build';
|
|
398
|
+
}
|
|
399
|
+
/** Written into the artifacts directory only after a build exits 0. */
|
|
400
|
+
export const BUILD_STAMP_FILENAME = '.build-ok.json';
|
|
401
|
+
const TYPESCRIPT_BUILD_INFO_FILENAME = 'tsconfig.tsbuildinfo';
|
|
402
|
+
/** Toolchain packages whose declared versions the stamp records. */
|
|
403
|
+
const STAMPED_TOOL_PACKAGES = ['typescript', 'svelte', '@sveltejs/package', 'vite', 'esbuild', 'tsup'];
|
|
404
|
+
function toPosixPath(host, path) {
|
|
405
|
+
return host.sep === '/' ? path : path.split(host.sep).join('/');
|
|
406
|
+
}
|
|
407
|
+
/** Tool configuration a package's build reads from its root, beside its tsconfig files. */
|
|
408
|
+
const BUILD_CONFIG_FILE = /^(?:svelte|vite|tsup|esbuild|rollup|vitest)\.config\.[cm]?[jt]s$/;
|
|
409
|
+
const TSCONFIG_FILE = /^tsconfig(?:\.[^/]*)?\.json$/;
|
|
410
|
+
/** A path-looking word in a build script (`scripts/build.ts`, `./tsconfig.svelte.json`, `src/x/y.css`). */
|
|
411
|
+
const SCRIPT_FILE_TOKEN = /^(?:\.{1,2}\/)?[\w@][\w@.\-/]*\.(?:[cm]?[jt]sx?|json|css|scss|svg|md)$/;
|
|
412
|
+
/** Removes `//` and block comments from tsconfig-style JSON without touching string contents. */
|
|
413
|
+
function stripJsonComments(text) {
|
|
414
|
+
let out = '';
|
|
415
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
416
|
+
const char = text[index];
|
|
417
|
+
if (char === '"') {
|
|
418
|
+
let end = index + 1;
|
|
419
|
+
while (end < text.length && text[end] !== '"')
|
|
420
|
+
end += text[end] === '\\' ? 2 : 1;
|
|
421
|
+
out += text.slice(index, end + 1);
|
|
422
|
+
index = end;
|
|
423
|
+
}
|
|
424
|
+
else if (char === '/' && text[index + 1] === '/') {
|
|
425
|
+
while (index < text.length && text[index] !== '\n')
|
|
426
|
+
index += 1;
|
|
427
|
+
out += '\n';
|
|
428
|
+
}
|
|
429
|
+
else if (char === '/' && text[index + 1] === '*') {
|
|
430
|
+
const end = text.indexOf('*/', index + 2);
|
|
431
|
+
index = end === -1 ? text.length : end + 1;
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
out += char;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return out.replace(/,(\s*[}\]])/g, '$1');
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Every file a tsconfig makes `tsc` read beyond the package's `src/`: the
|
|
441
|
+
* authored config, its relative `extends` chain, and the literal directories or
|
|
442
|
+
* files its `include`/`files` name. Generated build directories remain excluded:
|
|
443
|
+
* SvelteKit sync rewrites their metadata during the very build being stamped.
|
|
444
|
+
* A config that cannot be parsed contributes
|
|
445
|
+
* only itself: its bytes are still hashed, so any edit still invalidates.
|
|
446
|
+
*/
|
|
447
|
+
async function collectTsconfigInputs(host, packageRoot, artifactsDir, configPath, inputs) {
|
|
448
|
+
const configTop = host.paths.relative(packageRoot, configPath).split(host.sep)[0];
|
|
449
|
+
if (configTop === artifactsDir || SKIP_DIRS.has(configTop))
|
|
450
|
+
return;
|
|
451
|
+
if (inputs.has(configPath) || !(await host.files.exists(configPath)))
|
|
452
|
+
return;
|
|
453
|
+
inputs.add(configPath);
|
|
454
|
+
const TsconfigFieldsSchema = z.object({
|
|
455
|
+
extends: z.union([z.string(), z.array(z.string())]).optional(),
|
|
456
|
+
include: z.array(z.string()).optional(),
|
|
457
|
+
files: z.array(z.string()).optional()
|
|
458
|
+
}).loose();
|
|
459
|
+
const parsed = await settle(async () => TsconfigFieldsSchema.parse(JSON.parse(stripJsonComments(await readText(host.files, configPath)))));
|
|
460
|
+
if (!parsed.ok) {
|
|
461
|
+
infraDeployLogger(host.runtime).warn(`tsconfig ${configPath} is not parseable JSON; only its own bytes are hashed`, {
|
|
462
|
+
error: failureMessage(parsed.cause)
|
|
463
|
+
});
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
const config = parsed.value;
|
|
467
|
+
const parents = config.extends ? [config.extends].flat() : [];
|
|
468
|
+
for (const parent of parents) {
|
|
469
|
+
if (!parent.startsWith('.'))
|
|
470
|
+
continue;
|
|
471
|
+
const resolved = host.paths.join(host.paths.dirname(configPath), parent);
|
|
472
|
+
await collectTsconfigInputs(host, packageRoot, artifactsDir, resolved.endsWith('.json') ? resolved : `${resolved}.json`, inputs);
|
|
473
|
+
}
|
|
474
|
+
const named = [...(config.include ?? []), ...(config.files ?? [])];
|
|
475
|
+
for (const entry of named) {
|
|
476
|
+
const literal = [];
|
|
477
|
+
for (const segment of entry.split('/')) {
|
|
478
|
+
if (/[*?{[]/.test(segment))
|
|
479
|
+
break;
|
|
480
|
+
literal.push(segment);
|
|
481
|
+
}
|
|
482
|
+
const base = host.paths.join(host.paths.dirname(configPath), ...literal);
|
|
483
|
+
if (base === packageRoot || !isPathWithin(host, packageRoot, base))
|
|
484
|
+
continue;
|
|
485
|
+
const top = host.paths.relative(packageRoot, base).split(host.sep)[0];
|
|
486
|
+
if (top === artifactsDir || SKIP_DIRS.has(top))
|
|
487
|
+
continue;
|
|
488
|
+
if (!(await host.files.exists(base)))
|
|
489
|
+
continue;
|
|
490
|
+
if ((await followedStat(host, base)).kind === 'directory') {
|
|
491
|
+
for (const path of await filesInDirectory(host, base, isBuildInput))
|
|
492
|
+
inputs.add(path);
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
inputs.add(base);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Files the package's own build reads: its manifest,
|
|
501
|
+
* every `tsconfig*.json` (and their `extends`/`include` targets), tool config
|
|
502
|
+
* files (`vite.config.*`, `svelte.config.*`, ...), `scripts/`, every file the
|
|
503
|
+
* selected build script names, and every non-test, non-hidden file under
|
|
504
|
+
* `src/`. Workspace dependencies are folded in by {@link effectiveSourceHash}.
|
|
505
|
+
*/
|
|
506
|
+
async function packageBuildInputs(host, pkg) {
|
|
507
|
+
const packageRoot = pkg.dir;
|
|
508
|
+
const artifactsDir = resolveArtifactsDir(pkg);
|
|
509
|
+
const inputs = new Set(await filesInDirectory(host, host.paths.join(packageRoot, 'src'), isBuildInput));
|
|
510
|
+
for (const path of await filesInDirectory(host, host.paths.join(packageRoot, 'scripts'), isBuildInput))
|
|
511
|
+
inputs.add(path);
|
|
512
|
+
for (const entry of await host.files.list(packageRoot)) {
|
|
513
|
+
if (entry.kind !== 'file')
|
|
514
|
+
continue;
|
|
515
|
+
if (entry.name === 'package.json' || BUILD_CONFIG_FILE.test(entry.name)) {
|
|
516
|
+
inputs.add(host.paths.join(packageRoot, entry.name));
|
|
517
|
+
}
|
|
518
|
+
if (TSCONFIG_FILE.test(entry.name)) {
|
|
519
|
+
await collectTsconfigInputs(host, packageRoot, artifactsDir, host.paths.join(packageRoot, entry.name), inputs);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
const buildScript = pkg.packageJson.scripts?.[resolveBuildScriptName(pkg)] ?? '';
|
|
523
|
+
for (const token of buildScript.split(/[\s"'`=;&|()]+/)) {
|
|
524
|
+
if (!SCRIPT_FILE_TOKEN.test(token))
|
|
525
|
+
continue;
|
|
526
|
+
const path = host.paths.join(packageRoot, token);
|
|
527
|
+
const top = host.paths.relative(packageRoot, path).split(host.sep)[0];
|
|
528
|
+
if (top === artifactsDir || top === 'node_modules' || !isPathWithin(host, packageRoot, path))
|
|
529
|
+
continue;
|
|
530
|
+
if ((await host.files.exists(path)) && (await followedStat(host, path)).kind === 'file')
|
|
531
|
+
inputs.add(path);
|
|
532
|
+
if (TSCONFIG_FILE.test(token.split('/').pop() ?? '')) {
|
|
533
|
+
await collectTsconfigInputs(host, packageRoot, artifactsDir, path, inputs);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
return inputs;
|
|
537
|
+
}
|
|
538
|
+
async function computePackageSourceHash(host, packageRoot) {
|
|
539
|
+
const hash = host.sha256();
|
|
540
|
+
const pkg = await readPackageManifest(host, packageRoot);
|
|
541
|
+
const entries = [...await packageBuildInputs(host, pkg)]
|
|
542
|
+
.map((path) => ({ path: toPosixPath(host, host.paths.relative(packageRoot, path)), absolute: path }))
|
|
543
|
+
.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
544
|
+
for (const entry of entries) {
|
|
545
|
+
const fileHash = host.sha256().update(await readBytes(host.files, entry.absolute)).digestHex();
|
|
546
|
+
hash.update(`${entry.path}\0${fileHash}\n`);
|
|
547
|
+
}
|
|
548
|
+
return hash.digestHex();
|
|
549
|
+
}
|
|
550
|
+
/** One memo for the build inputs and hashes read in a plan or independent postflight. */
|
|
551
|
+
const effectiveHashMemos = new WeakMap();
|
|
552
|
+
function dependencyHashMemo(ownHashes) {
|
|
553
|
+
let memo = effectiveHashMemos.get(ownHashes);
|
|
554
|
+
if (!memo) {
|
|
555
|
+
memo = { effective: new Map(), edges: new Map() };
|
|
556
|
+
effectiveHashMemos.set(ownHashes, memo);
|
|
557
|
+
}
|
|
558
|
+
return memo;
|
|
559
|
+
}
|
|
560
|
+
function buildDependencyNames(host, pkg, packages, memo) {
|
|
561
|
+
let pending = memo.edges.get(pkg.name);
|
|
562
|
+
if (!pending) {
|
|
563
|
+
pending = workspaceDependencyEdges(host, pkg, packages)
|
|
564
|
+
.then(({ inputs }) => inputs.filter((name) => packages.has(name)));
|
|
565
|
+
memo.edges.set(pkg.name, pending);
|
|
566
|
+
}
|
|
567
|
+
return pending;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* A package's own source hash folded with its workspace dependencies'
|
|
571
|
+
* effective hashes, computed once per package in linear time.
|
|
572
|
+
*
|
|
573
|
+
* Packages that import each other (a tolerated mutual-import cycle) form a
|
|
574
|
+
* strongly connected component; every member's hash covers the whole
|
|
575
|
+
* component and everything it depends on, so the result does not depend on
|
|
576
|
+
* which package the walk started from. A package outside any cycle hashes
|
|
577
|
+
* exactly as it always has: its own hash, then `\0name\0effective` for each
|
|
578
|
+
* declared dependency in order.
|
|
579
|
+
*/
|
|
580
|
+
async function effectiveSourceHash(host, pkg, packages, ownHashes) {
|
|
581
|
+
const scope = dependencyHashMemo(ownHashes);
|
|
582
|
+
const memo = scope.effective;
|
|
583
|
+
const known = memo.get(pkg.name);
|
|
584
|
+
if (known !== undefined)
|
|
585
|
+
return known;
|
|
586
|
+
// Hash the planner's build inputs. Imported optional peers participate in
|
|
587
|
+
// freshness without becoming required members of the build closure.
|
|
588
|
+
const graph = new Map();
|
|
589
|
+
const pending = [pkg.name];
|
|
590
|
+
const queued = new Set(pending);
|
|
591
|
+
for (let cursor = 0; cursor < pending.length; cursor += 1) {
|
|
592
|
+
const name = pending[cursor];
|
|
593
|
+
if (memo.has(name))
|
|
594
|
+
continue;
|
|
595
|
+
const dependencies = await buildDependencyNames(host, packages.get(name), packages, scope);
|
|
596
|
+
graph.set(name, dependencies);
|
|
597
|
+
for (const dependency of dependencies) {
|
|
598
|
+
if (memo.has(dependency) || queued.has(dependency))
|
|
599
|
+
continue;
|
|
600
|
+
queued.add(dependency);
|
|
601
|
+
pending.push(dependency);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
// Another concurrent writer may have completed this component during I/O.
|
|
605
|
+
const completed = memo.get(pkg.name);
|
|
606
|
+
if (completed !== undefined)
|
|
607
|
+
return completed;
|
|
608
|
+
const edges = (name) => graph.get(name) ?? [];
|
|
609
|
+
// Tarjan's SCC over the packages reachable from `pkg` that are not yet
|
|
610
|
+
// memoized; components come out dependencies-first.
|
|
611
|
+
const index = new Map();
|
|
612
|
+
const low = new Map();
|
|
613
|
+
const onStack = new Set();
|
|
614
|
+
const stack = [];
|
|
615
|
+
const components = [];
|
|
616
|
+
let counter = 0;
|
|
617
|
+
const connect = (name) => {
|
|
618
|
+
index.set(name, counter);
|
|
619
|
+
low.set(name, counter);
|
|
620
|
+
counter += 1;
|
|
621
|
+
stack.push(name);
|
|
622
|
+
onStack.add(name);
|
|
623
|
+
for (const dep of edges(name)) {
|
|
624
|
+
if (memo.has(dep))
|
|
625
|
+
continue;
|
|
626
|
+
if (!index.has(dep)) {
|
|
627
|
+
connect(dep);
|
|
628
|
+
low.set(name, Math.min(low.get(name), low.get(dep)));
|
|
629
|
+
}
|
|
630
|
+
else if (onStack.has(dep)) {
|
|
631
|
+
low.set(name, Math.min(low.get(name), index.get(dep)));
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
if (low.get(name) === index.get(name)) {
|
|
635
|
+
const component = [];
|
|
636
|
+
let member;
|
|
637
|
+
do {
|
|
638
|
+
member = stack.pop();
|
|
639
|
+
onStack.delete(member);
|
|
640
|
+
component.push(member);
|
|
641
|
+
} while (member !== name);
|
|
642
|
+
components.push(component);
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
connect(pkg.name);
|
|
646
|
+
const ownOf = async (name) => {
|
|
647
|
+
let own = ownHashes.get(name);
|
|
648
|
+
if (own === undefined) {
|
|
649
|
+
own = await computePackageSourceHash(host, packages.get(name).dir);
|
|
650
|
+
ownHashes.set(name, own);
|
|
651
|
+
}
|
|
652
|
+
return own;
|
|
653
|
+
};
|
|
654
|
+
for (const component of components) {
|
|
655
|
+
const members = new Set(component);
|
|
656
|
+
const selfLoop = component.length === 1 && edges(component[0]).includes(component[0]);
|
|
657
|
+
if (component.length === 1 && !selfLoop) {
|
|
658
|
+
const name = component[0];
|
|
659
|
+
const hash = host.sha256().update(await ownOf(name));
|
|
660
|
+
for (const dep of edges(name))
|
|
661
|
+
hash.update(`\0${dep}\0${memo.get(dep)}`);
|
|
662
|
+
memo.set(name, hash.digestHex());
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
// One hash for the whole component: every member's own hash and every
|
|
666
|
+
// dependency the component reaches outside itself, in a fixed order.
|
|
667
|
+
const componentHash = host.sha256().update('scc');
|
|
668
|
+
for (const name of [...component].sort())
|
|
669
|
+
componentHash.update(`\0${name}\0${await ownOf(name)}`);
|
|
670
|
+
const external = new Set();
|
|
671
|
+
for (const name of component)
|
|
672
|
+
for (const dep of edges(name))
|
|
673
|
+
if (!members.has(dep))
|
|
674
|
+
external.add(dep);
|
|
675
|
+
for (const dep of [...external].sort())
|
|
676
|
+
componentHash.update(`\0dep\0${dep}\0${memo.get(dep)}`);
|
|
677
|
+
const shared = componentHash.digestHex();
|
|
678
|
+
// Publish the whole component without yielding: concurrent walks must
|
|
679
|
+
// never observe only part of a cycle as memoized.
|
|
680
|
+
for (const name of component) {
|
|
681
|
+
memo.set(name, host.sha256().update(ownHashes.get(name)).update(`\0scc\0${shared}`).digestHex());
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return memo.get(pkg.name);
|
|
685
|
+
}
|
|
686
|
+
/** What a stamp records about a package's workspace dependencies: name -> effective source hash. */
|
|
687
|
+
const RecordedDependencyHashesSchema = z.record(z.string(), z.string());
|
|
688
|
+
async function workspaceDependencyHashes(host, pkg, packages, ownHashes) {
|
|
689
|
+
const recorded = {};
|
|
690
|
+
if (!packages)
|
|
691
|
+
return recorded;
|
|
692
|
+
for (const name of await buildDependencyNames(host, pkg, packages, dependencyHashMemo(ownHashes))) {
|
|
693
|
+
const dependency = packages.get(name);
|
|
694
|
+
if (!dependency)
|
|
695
|
+
continue;
|
|
696
|
+
recorded[name] = await effectiveSourceHash(host, dependency, packages, ownHashes);
|
|
697
|
+
}
|
|
698
|
+
return recorded;
|
|
699
|
+
}
|
|
700
|
+
/** Every file the build emitted under the artifacts directory (the stamp itself excluded). */
|
|
701
|
+
async function listEmittedFiles(host, artifactsDirectory) {
|
|
702
|
+
const files = [];
|
|
703
|
+
const visit = async (directory) => {
|
|
704
|
+
for (const entry of await host.files.list(directory)) {
|
|
705
|
+
const entryPath = host.paths.join(directory, entry.name);
|
|
706
|
+
if (entry.kind === 'directory') {
|
|
707
|
+
await visit(entryPath);
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
const relativePath = toPosixPath(host, host.paths.relative(artifactsDirectory, entryPath));
|
|
711
|
+
if (relativePath === BUILD_STAMP_FILENAME)
|
|
712
|
+
continue;
|
|
713
|
+
const stats = await followedStat(host, entryPath);
|
|
714
|
+
if (stats.kind === 'file')
|
|
715
|
+
files.push({ path: relativePath, size: stats.size });
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
if (await host.files.exists(artifactsDirectory))
|
|
719
|
+
await visit(artifactsDirectory);
|
|
720
|
+
return files.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
721
|
+
}
|
|
722
|
+
/** Declared versions of the toolchain packages a stamp records. */
|
|
723
|
+
const StampedToolVersionsSchema = z.record(z.string(), z.string());
|
|
724
|
+
function declaredToolVersions(packageJson) {
|
|
725
|
+
const versions = {};
|
|
726
|
+
for (const name of STAMPED_TOOL_PACKAGES) {
|
|
727
|
+
const declared = packageJson.devDependencies?.[name] ??
|
|
728
|
+
packageJson.dependencies?.[name] ??
|
|
729
|
+
packageJson.peerDependencies?.[name];
|
|
730
|
+
if (declared)
|
|
731
|
+
versions[name] = declared;
|
|
732
|
+
}
|
|
733
|
+
return versions;
|
|
734
|
+
}
|
|
735
|
+
async function readPackageManifest(host, packageDir) {
|
|
736
|
+
const packageJson = await readJson(host, host.paths.join(packageDir, 'package.json'));
|
|
737
|
+
if (!packageJson)
|
|
738
|
+
throw new Error(`Package has no manifest: ${packageDir}`);
|
|
739
|
+
return { name: packageJson.name ?? packageDir, dir: packageDir, packageJson };
|
|
740
|
+
}
|
|
741
|
+
/** Removes the build-success stamp, so an interrupted or failed build can never look fresh. */
|
|
742
|
+
async function removeBuildStamp(host, pkg) {
|
|
743
|
+
await removeIfPresent(host.files, host.paths.join(pkg.dir, resolveArtifactsDir(pkg), BUILD_STAMP_FILENAME));
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Records a successful build: the source content hash it was built from and the
|
|
747
|
+
* exact files it emitted. Callers write it only after the build exited 0.
|
|
748
|
+
*/
|
|
749
|
+
export async function writeBuildOkStamp(packageDir,
|
|
750
|
+
/** The runtime whose logger reports a build input the hash could only read as bytes. */
|
|
751
|
+
runtime, packages, dependencySourceHashes = new Map()) {
|
|
752
|
+
const host = await resolveArtifactHost(runtime);
|
|
753
|
+
const pkg = await readPackageManifest(host, packageDir);
|
|
754
|
+
const artifactsDirectory = host.paths.join(pkg.dir, resolveArtifactsDir(pkg));
|
|
755
|
+
const stamp = {
|
|
756
|
+
sourceHash: await computePackageSourceHash(host, pkg.dir),
|
|
757
|
+
dependencies: await workspaceDependencyHashes(host, pkg, packages, dependencySourceHashes),
|
|
758
|
+
toolVersions: declaredToolVersions(pkg.packageJson),
|
|
759
|
+
files: await listEmittedFiles(host, artifactsDirectory)
|
|
760
|
+
};
|
|
761
|
+
// A build that exited 0 but emitted nothing (an honest outcome the freshness
|
|
762
|
+
// check reports as "no artifacts") still earns its stamp: recording it must
|
|
763
|
+
// not crash on the absent directory and mask the build's real result.
|
|
764
|
+
await host.files.mkdir(artifactsDirectory, { recursive: true });
|
|
765
|
+
await host.files.writeFile(host.paths.join(artifactsDirectory, BUILD_STAMP_FILENAME), `${JSON.stringify(stamp, null, '\t')}\n`);
|
|
766
|
+
return stamp;
|
|
767
|
+
}
|
|
768
|
+
/** The admitted shape of a build stamp, parsed once at the boundary. */
|
|
769
|
+
const BuildStampSchema = z.object({
|
|
770
|
+
sourceHash: z.string(),
|
|
771
|
+
dependencies: z.record(z.string(), z.string()),
|
|
772
|
+
toolVersions: z.record(z.string(), z.string()),
|
|
773
|
+
files: z.array(z.object({ path: z.string(), size: z.number() }).loose())
|
|
774
|
+
}).loose();
|
|
775
|
+
function parseBuildStamp(text) {
|
|
776
|
+
const parsed = BuildStampSchema.safeParse(JSON.parse(text));
|
|
777
|
+
if (parsed.success) {
|
|
778
|
+
// The schema admits extra keys from older and newer writers; the stamp
|
|
779
|
+
// is the four typed fields, constructed explicitly instead of cast.
|
|
780
|
+
const { sourceHash, dependencies, toolVersions, files } = parsed.data;
|
|
781
|
+
return {
|
|
782
|
+
sourceHash,
|
|
783
|
+
dependencies,
|
|
784
|
+
toolVersions,
|
|
785
|
+
files: files.map((file) => ({ path: file.path, size: file.size }))
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
// An unparseable or malformed stamp is an invalid stamp: the caller reports
|
|
789
|
+
// it as not fresh.
|
|
790
|
+
return undefined;
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* The single freshness decision every caller shares: `assertDeployWorkspacePreflight`
|
|
794
|
+
* uses it to refuse a deploy over stale inputs, and the build orchestration
|
|
795
|
+
* below uses the same predicate to skip a closure member that never went
|
|
796
|
+
* stale. Fresh means a build-success stamp exists, its source hash equals the
|
|
797
|
+
* hash of the package's current build inputs, and every file it lists still
|
|
798
|
+
* exists at the recorded size. There is no timestamp heuristic: a package with
|
|
799
|
+
* no stamp (never built, failed build, interrupted build) is not fresh.
|
|
800
|
+
*/
|
|
801
|
+
async function checkWorkspacePackageFreshness(host, pkg, packages, ownHashes = new Map()) {
|
|
802
|
+
const resolvedPackageRoot = await realpathChecked(host.files, pkg.dir);
|
|
803
|
+
const artifactsDir = resolveArtifactsDir(pkg);
|
|
804
|
+
const artifactsDirectory = host.paths.join(resolvedPackageRoot, artifactsDir);
|
|
805
|
+
const result = (reason, artifactsMissing = false) => ({
|
|
806
|
+
fresh: reason === undefined,
|
|
807
|
+
artifactsDir,
|
|
808
|
+
resolvedPackageRoot,
|
|
809
|
+
reason,
|
|
810
|
+
artifactsMissing
|
|
811
|
+
});
|
|
812
|
+
const artifactNames = (await host.files.exists(artifactsDirectory))
|
|
813
|
+
? (await host.files.list(artifactsDirectory)).map((entry) => entry.name)
|
|
814
|
+
: [];
|
|
815
|
+
if (artifactNames.every((name) => name === BUILD_STAMP_FILENAME)) {
|
|
816
|
+
return result(`${artifactsDir} has no artifacts`, true);
|
|
817
|
+
}
|
|
818
|
+
const stampPath = host.paths.join(artifactsDirectory, BUILD_STAMP_FILENAME);
|
|
819
|
+
if (!(await host.files.exists(stampPath))) {
|
|
820
|
+
return result(`${artifactsDir}/${BUILD_STAMP_FILENAME} is missing, so no successful build is recorded`);
|
|
821
|
+
}
|
|
822
|
+
const stamp = parseBuildStamp(await readText(host.files, stampPath));
|
|
823
|
+
if (!stamp)
|
|
824
|
+
return result(`${artifactsDir}/${BUILD_STAMP_FILENAME} is not a valid build stamp`);
|
|
825
|
+
if (stamp.files.length === 0)
|
|
826
|
+
return result(`${artifactsDir}/${BUILD_STAMP_FILENAME} lists no emitted files`);
|
|
827
|
+
if (stamp.sourceHash !== (await computePackageSourceHash(host, resolvedPackageRoot))) {
|
|
828
|
+
return result(`package sources changed since the last successful build`);
|
|
829
|
+
}
|
|
830
|
+
const currentDependencies = await workspaceDependencyHashes(host, pkg, packages, ownHashes);
|
|
831
|
+
const dependencyNames = new Set([...Object.keys(stamp.dependencies), ...Object.keys(currentDependencies)]);
|
|
832
|
+
for (const name of [...dependencyNames].sort()) {
|
|
833
|
+
if (stamp.dependencies[name] !== currentDependencies[name]) {
|
|
834
|
+
return result(`workspace dependency ${name} changed since the last successful build`);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
for (const file of stamp.files) {
|
|
838
|
+
const path = host.paths.join(artifactsDirectory, file.path);
|
|
839
|
+
if (!isPathWithin(host, artifactsDirectory, path)) {
|
|
840
|
+
return result(`${artifactsDir}/${BUILD_STAMP_FILENAME} lists a path outside ${artifactsDir}: ${file.path}`);
|
|
841
|
+
}
|
|
842
|
+
const stats = await settle(() => followedStat(host, path));
|
|
843
|
+
if (!stats.ok) {
|
|
844
|
+
// An emitted file that vanished is not fresh; any other stat failure is real.
|
|
845
|
+
if (isMissingPathError(stats.cause))
|
|
846
|
+
return result(`emitted file is missing: ${artifactsDir}/${file.path}`);
|
|
847
|
+
throw stats.cause;
|
|
848
|
+
}
|
|
849
|
+
if (stats.value.kind !== 'file')
|
|
850
|
+
return result(`emitted file is not a regular file: ${artifactsDir}/${file.path}`);
|
|
851
|
+
const size = stats.value.size;
|
|
852
|
+
if (size !== file.size) {
|
|
853
|
+
return result(`emitted file changed size (${file.size} -> ${size}): ${artifactsDir}/${file.path}`);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
const buildInfoReason = await typeScriptBuildInfoRefusal(host, host.paths.join(artifactsDirectory, TYPESCRIPT_BUILD_INFO_FILENAME));
|
|
857
|
+
if (buildInfoReason)
|
|
858
|
+
return result(buildInfoReason);
|
|
859
|
+
return result(undefined);
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* A stamped build whose incremental state still records pending emit or
|
|
863
|
+
* diagnostics is not a clean build, whatever the stamp says.
|
|
864
|
+
*/
|
|
865
|
+
async function typeScriptBuildInfoRefusal(host, tsBuildInfoPath) {
|
|
866
|
+
if (!(await host.files.exists(tsBuildInfoPath)))
|
|
867
|
+
return undefined;
|
|
868
|
+
const TsBuildInfoSchema = z.object({
|
|
869
|
+
errors: z.boolean().optional(),
|
|
870
|
+
affectedFilesPendingEmit: z.array(z.unknown()).optional(),
|
|
871
|
+
semanticDiagnosticsPerFile: z.array(z.unknown()).optional()
|
|
872
|
+
}).loose();
|
|
873
|
+
const buildInfo = TsBuildInfoSchema.safeParse(JSON.parse(await readText(host.files, tsBuildInfoPath)));
|
|
874
|
+
if (!buildInfo.success) {
|
|
875
|
+
// An unreadable incremental state cannot vouch for the build.
|
|
876
|
+
return `${TYPESCRIPT_BUILD_INFO_FILENAME} is not valid JSON`;
|
|
877
|
+
}
|
|
878
|
+
if (buildInfo.data.errors === true)
|
|
879
|
+
return `${TYPESCRIPT_BUILD_INFO_FILENAME} records compile errors`;
|
|
880
|
+
if (Array.isArray(buildInfo.data.affectedFilesPendingEmit) && buildInfo.data.affectedFilesPendingEmit.length > 0) {
|
|
881
|
+
return `${TYPESCRIPT_BUILD_INFO_FILENAME} records files pending emit`;
|
|
882
|
+
}
|
|
883
|
+
// tsc lists a bare file id for a clean file and `[id, diagnostics]` for one with errors.
|
|
884
|
+
if (Array.isArray(buildInfo.data.semanticDiagnosticsPerFile) &&
|
|
885
|
+
buildInfo.data.semanticDiagnosticsPerFile.some((entry) => Array.isArray(entry))) {
|
|
886
|
+
return `${TYPESCRIPT_BUILD_INFO_FILENAME} records semantic diagnostics`;
|
|
887
|
+
}
|
|
888
|
+
return undefined;
|
|
889
|
+
}
|
|
890
|
+
async function buildPackage(host, workspaceRoot, pkg, execution, packages, dependencySourceHashes, onLine) {
|
|
891
|
+
const scriptName = resolveBuildScriptName(pkg);
|
|
892
|
+
onLine?.('stdout', `[workspace-artifacts] building ${pkg.name}`);
|
|
893
|
+
const diagnostics = createWorkspaceDiagnosticTail();
|
|
894
|
+
// A prior stamp must never outlive the start of a rebuild: only a build that
|
|
895
|
+
// exits 0 writes one, so an interrupted build reads as not fresh.
|
|
896
|
+
await removeBuildStamp(host, pkg);
|
|
897
|
+
let result;
|
|
898
|
+
try {
|
|
899
|
+
result = await runRuntimeCommand({
|
|
900
|
+
runtime: host.runtime,
|
|
901
|
+
execution,
|
|
902
|
+
command: 'bun',
|
|
903
|
+
killTree: true,
|
|
904
|
+
args: ['run', '--cwd', pkg.dir, scriptName],
|
|
905
|
+
cwd: workspaceRoot,
|
|
906
|
+
env: await registryTokenEnv(host.runtime),
|
|
907
|
+
onLine: (stream, line) => {
|
|
908
|
+
const sanitized = diagnostics.push(stream, line);
|
|
909
|
+
onLine?.(stream, sanitized);
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
catch (error) {
|
|
914
|
+
await invalidateFailedBuild(host, pkg);
|
|
915
|
+
throw error;
|
|
916
|
+
}
|
|
917
|
+
if (result.code !== 0) {
|
|
918
|
+
await invalidateFailedBuild(host, pkg);
|
|
919
|
+
throw new Error(`Workspace artifact build failed for ${pkg.name} (exit=${result.code ?? 'unknown'})\n` +
|
|
920
|
+
`Compiler diagnostics (redacted tail):\n${diagnostics.render()}`);
|
|
921
|
+
}
|
|
922
|
+
await writeBuildOkStamp(pkg.dir, host.runtime, packages, dependencySourceHashes);
|
|
923
|
+
}
|
|
924
|
+
/** A failed build leaves neither its stamp nor its incremental state, so the next run rebuilds from scratch. */
|
|
925
|
+
async function invalidateFailedBuild(host, pkg) {
|
|
926
|
+
const artifactsDirectory = host.paths.join(pkg.dir, resolveArtifactsDir(pkg));
|
|
927
|
+
await removeIfPresent(host.files, host.paths.join(artifactsDirectory, BUILD_STAMP_FILENAME));
|
|
928
|
+
await removeIfPresent(host.files, host.paths.join(artifactsDirectory, TYPESCRIPT_BUILD_INFO_FILENAME));
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Builds `plan` with up to `concurrency` packages compiling at once.
|
|
932
|
+
* Independent branches of the dependency graph (declared or source-discovered
|
|
933
|
+
* edges, the same edge set the planner used) run concurrently; a mutual-import
|
|
934
|
+
* cycle — the class `orderCycleMembersByWorkspaceDependencies` already
|
|
935
|
+
* resolves into one contiguous, order-sensitive run — still builds its
|
|
936
|
+
* members serially, in the plan's existing order, so a tolerated cycle's
|
|
937
|
+
* behaviour is unchanged. `run` decides per package whether to actually spawn
|
|
938
|
+
* a build (it applies the freshness skip); this function only owns the safe
|
|
939
|
+
* scheduling.
|
|
940
|
+
*/
|
|
941
|
+
async function runWorkspaceBuildPlan(host, plan, packages, concurrency, run) {
|
|
942
|
+
if (plan.length === 0)
|
|
943
|
+
return;
|
|
944
|
+
const dependencyCache = new Map();
|
|
945
|
+
for (const pkg of plan) {
|
|
946
|
+
dependencyCache.set(pkg.name, await workspaceDependencyEdges(host, pkg, packages));
|
|
947
|
+
}
|
|
948
|
+
const dependenciesOf = (pkg) => {
|
|
949
|
+
const deps = dependencyCache.get(pkg.name);
|
|
950
|
+
if (!deps) {
|
|
951
|
+
throw new Error(`Workspace dependency edges were not loaded for ${pkg.name}`);
|
|
952
|
+
}
|
|
953
|
+
return deps;
|
|
954
|
+
};
|
|
955
|
+
const components = groupWorkspaceBuildPlanIntoComponents(plan, dependenciesOf);
|
|
956
|
+
const remaining = components.map((component) => new Set(component.dependsOn));
|
|
957
|
+
const waiters = components.map(() => []);
|
|
958
|
+
remaining.forEach((deps, index) => {
|
|
959
|
+
for (const dep of deps)
|
|
960
|
+
waiters[dep].push(index);
|
|
961
|
+
});
|
|
962
|
+
await new Promise((resolveAll, rejectAll) => {
|
|
963
|
+
let settled = false;
|
|
964
|
+
let active = 0;
|
|
965
|
+
let completed = 0;
|
|
966
|
+
const queue = components.map((_, index) => index).filter((index) => remaining[index].size === 0);
|
|
967
|
+
let cursor = 0;
|
|
968
|
+
const fail = (failure) => {
|
|
969
|
+
if (settled)
|
|
970
|
+
return;
|
|
971
|
+
settled = true;
|
|
972
|
+
// The caught failure travels whole into the combined rejection.
|
|
973
|
+
rejectAll(failure);
|
|
974
|
+
};
|
|
975
|
+
// One component's build: outcome-based, so a failure travels whole into the
|
|
976
|
+
// combined rejection and completion always advances the scheduler.
|
|
977
|
+
const runComponent = async (index) => {
|
|
978
|
+
const outcome = await settle(async () => {
|
|
979
|
+
for (const pkg of components[index].members)
|
|
980
|
+
await run(pkg);
|
|
981
|
+
});
|
|
982
|
+
if (!outcome.ok)
|
|
983
|
+
fail(outcome.cause);
|
|
984
|
+
active -= 1;
|
|
985
|
+
completed += 1;
|
|
986
|
+
for (const waiter of waiters[index]) {
|
|
987
|
+
remaining[waiter].delete(index);
|
|
988
|
+
if (remaining[waiter].size === 0)
|
|
989
|
+
queue.push(waiter);
|
|
990
|
+
}
|
|
991
|
+
if (completed === components.length) {
|
|
992
|
+
if (!settled) {
|
|
993
|
+
settled = true;
|
|
994
|
+
resolveAll();
|
|
995
|
+
}
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
pump();
|
|
999
|
+
};
|
|
1000
|
+
const pump = () => {
|
|
1001
|
+
if (settled)
|
|
1002
|
+
return;
|
|
1003
|
+
while (active < concurrency && cursor < queue.length) {
|
|
1004
|
+
const index = queue[cursor];
|
|
1005
|
+
cursor += 1;
|
|
1006
|
+
active += 1;
|
|
1007
|
+
void runComponent(index);
|
|
1008
|
+
}
|
|
1009
|
+
};
|
|
1010
|
+
pump();
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Groups `plan` into its dependency components on the SAME `.all` edge set
|
|
1015
|
+
* the planner used: a lone acyclic package is its own single-member
|
|
1016
|
+
* component, and a mutual-import cycle collapses into one multi-member
|
|
1017
|
+
* component (mirroring `findStronglyConnectedComponents`'s use inside
|
|
1018
|
+
* `orderCycleMembersByWorkspaceDependencies`). Two components with no path
|
|
1019
|
+
* between them can build at the same time.
|
|
1020
|
+
*/
|
|
1021
|
+
function groupWorkspaceBuildPlanIntoComponents(plan, dependenciesOf) {
|
|
1022
|
+
const planNames = new Set(plan.map((pkg) => pkg.name));
|
|
1023
|
+
const allEdges = new Map();
|
|
1024
|
+
for (const pkg of plan) {
|
|
1025
|
+
allEdges.set(pkg.name, dependenciesOf(pkg).all.filter((name) => planNames.has(name)));
|
|
1026
|
+
}
|
|
1027
|
+
const componentByName = findStronglyConnectedComponents(plan.map((pkg) => pkg.name), allEdges);
|
|
1028
|
+
const componentOrder = [];
|
|
1029
|
+
const seen = new Set();
|
|
1030
|
+
const membersByComponent = new Map();
|
|
1031
|
+
for (const pkg of plan) {
|
|
1032
|
+
const component = componentByName.get(pkg.name);
|
|
1033
|
+
if (component === undefined)
|
|
1034
|
+
continue;
|
|
1035
|
+
if (!seen.has(component)) {
|
|
1036
|
+
seen.add(component);
|
|
1037
|
+
componentOrder.push(component);
|
|
1038
|
+
}
|
|
1039
|
+
const members = membersByComponent.get(component) ?? [];
|
|
1040
|
+
members.push(pkg);
|
|
1041
|
+
membersByComponent.set(component, members);
|
|
1042
|
+
}
|
|
1043
|
+
const dependsOnByComponent = new Map();
|
|
1044
|
+
for (const component of componentOrder)
|
|
1045
|
+
dependsOnByComponent.set(component, new Set());
|
|
1046
|
+
for (const [name, deps] of allEdges) {
|
|
1047
|
+
const component = componentByName.get(name);
|
|
1048
|
+
if (component === undefined)
|
|
1049
|
+
continue;
|
|
1050
|
+
for (const dep of deps) {
|
|
1051
|
+
const depComponent = componentByName.get(dep);
|
|
1052
|
+
if (depComponent === undefined || depComponent === component)
|
|
1053
|
+
continue;
|
|
1054
|
+
dependsOnByComponent.get(component)?.add(depComponent);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
return componentOrder.map((component) => ({
|
|
1058
|
+
members: membersByComponent.get(component) ?? [],
|
|
1059
|
+
dependsOn: dependsOnByComponent.get(component) ?? new Set()
|
|
1060
|
+
}));
|
|
1061
|
+
}
|
|
1062
|
+
/** Independent workspace package builds run this many at a time (see `runWorkspaceBuildPlan`). */
|
|
1063
|
+
const DEFAULT_WORKSPACE_BUILD_CONCURRENCY = 4;
|
|
1064
|
+
async function buildWorkspacePlanWithFreshnessSkip(host, workspaceRoot, plan, packages, execution, onLine) {
|
|
1065
|
+
// Source hashes do not change while a plan builds (builds emit only into artifact directories).
|
|
1066
|
+
const ownHashes = new Map();
|
|
1067
|
+
await runWorkspaceBuildPlan(host, plan, packages, DEFAULT_WORKSPACE_BUILD_CONCURRENCY, async (pkg) => {
|
|
1068
|
+
const freshness = await checkWorkspacePackageFreshness(host, pkg, packages, ownHashes);
|
|
1069
|
+
if (freshness.fresh) {
|
|
1070
|
+
onLine?.('stdout', `[workspace-artifacts] skip ${pkg.name}: ${freshness.artifactsDir} is already fresh`);
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
await buildPackage(host, workspaceRoot, pkg, execution, packages, ownHashes, onLine);
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
async function discoverSvelteKitApps(host, workspaceRoot) {
|
|
1077
|
+
const appsRoot = host.paths.join(workspaceRoot, 'apps');
|
|
1078
|
+
if (!(await host.files.exists(appsRoot)))
|
|
1079
|
+
return [];
|
|
1080
|
+
const apps = [];
|
|
1081
|
+
for (const entry of await host.files.list(appsRoot)) {
|
|
1082
|
+
if (entry.kind !== 'directory')
|
|
1083
|
+
continue;
|
|
1084
|
+
const dir = host.paths.join(appsRoot, entry.name);
|
|
1085
|
+
const hasJs = await host.files.exists(host.paths.join(dir, 'svelte.config.js'));
|
|
1086
|
+
const hasTs = await host.files.exists(host.paths.join(dir, 'svelte.config.ts'));
|
|
1087
|
+
if (!hasJs && !hasTs)
|
|
1088
|
+
continue;
|
|
1089
|
+
apps.push({ name: `apps/${entry.name}`, dir });
|
|
1090
|
+
}
|
|
1091
|
+
return apps.sort((a, b) => a.name.localeCompare(b.name));
|
|
1092
|
+
}
|
|
1093
|
+
async function needsSvelteKitSync(host, app) {
|
|
1094
|
+
return !(await host.files.exists(host.paths.join(app.dir, '.svelte-kit', 'tsconfig.json')));
|
|
1095
|
+
}
|
|
1096
|
+
async function syncSvelteKitApp(host, workspaceRoot, app, execution, onLine) {
|
|
1097
|
+
onLine?.('stdout', `[svelte-kit-sync] syncing ${app.name}`);
|
|
1098
|
+
const result = await runRuntimeCommand({
|
|
1099
|
+
runtime: host.runtime,
|
|
1100
|
+
execution,
|
|
1101
|
+
command: 'bun',
|
|
1102
|
+
args: ['--bun', 'svelte-kit', 'sync'],
|
|
1103
|
+
cwd: app.dir,
|
|
1104
|
+
env: await registryTokenEnv(host.runtime),
|
|
1105
|
+
onLine
|
|
1106
|
+
});
|
|
1107
|
+
if (result.code !== 0) {
|
|
1108
|
+
throw new Error(`SvelteKit metadata sync failed for ${app.name} (exit=${result.code ?? 'unknown'})`);
|
|
1109
|
+
}
|
|
1110
|
+
const syncedConfig = host.paths.join(app.dir, '.svelte-kit', 'tsconfig.json');
|
|
1111
|
+
if (!(await host.files.exists(syncedConfig))) {
|
|
1112
|
+
throw new Error(`SvelteKit metadata sync did not produce ${host.paths.relative(workspaceRoot, syncedConfig)}`);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
async function syncMissingSvelteKitMetadataForWorkspace(host, workspaceRoot, appDir, execution, onLine) {
|
|
1116
|
+
const apps = await discoverSvelteKitApps(host, workspaceRoot);
|
|
1117
|
+
const targetFirst = apps.sort((a, b) => {
|
|
1118
|
+
if (a.dir === appDir)
|
|
1119
|
+
return -1;
|
|
1120
|
+
if (b.dir === appDir)
|
|
1121
|
+
return 1;
|
|
1122
|
+
return a.name.localeCompare(b.name);
|
|
1123
|
+
});
|
|
1124
|
+
const missing = [];
|
|
1125
|
+
for (const app of targetFirst) {
|
|
1126
|
+
if (await needsSvelteKitSync(host, app))
|
|
1127
|
+
missing.push(app);
|
|
1128
|
+
}
|
|
1129
|
+
if (missing.length === 0)
|
|
1130
|
+
return;
|
|
1131
|
+
onLine?.('stdout', `[svelte-kit-sync] preparing generated metadata for ${missing.length} SvelteKit app(s)`);
|
|
1132
|
+
for (const app of missing) {
|
|
1133
|
+
await syncSvelteKitApp(host, workspaceRoot, app, execution, onLine);
|
|
1134
|
+
}
|
|
1135
|
+
onLine?.('stdout', `[svelte-kit-sync] metadata ready for ${missing.length} SvelteKit app(s)`);
|
|
1136
|
+
}
|
|
1137
|
+
/**
|
|
1138
|
+
* Workspace dependency edges for a package: the union of declared
|
|
1139
|
+
* `dependencies` + `peerDependencies` (the *-svelte packages declare their
|
|
1140
|
+
* core package as a peer dependency, so `dependencies` alone misses e.g.
|
|
1141
|
+
* account-svelte -> @vibesdotdev/account) and the package's own source
|
|
1142
|
+
* imports (catches undeclared imports such as chat-svelte importing
|
|
1143
|
+
* `@vibesdotdev/chat/client`).
|
|
1144
|
+
*/
|
|
1145
|
+
function selectorResolvesWorkspacePackage(pkg, selector) {
|
|
1146
|
+
if (selector.startsWith('workspace:'))
|
|
1147
|
+
return true;
|
|
1148
|
+
if (!pkg.packageJson.version)
|
|
1149
|
+
return true;
|
|
1150
|
+
return Boolean(validRange(selector) && satisfies(pkg.packageJson.version, selector));
|
|
1151
|
+
}
|
|
1152
|
+
/** Build scripts/configuration may use development dependencies; public entry points may not. */
|
|
1153
|
+
function isBuildOnlyImportSource(host, pkg, path) {
|
|
1154
|
+
const relative = toPosixPath(host, host.paths.relative(pkg.dir, path));
|
|
1155
|
+
const buildOnly = relative.startsWith('scripts/') || BUILD_CONFIG_FILE.test(relative);
|
|
1156
|
+
if (!buildOnly)
|
|
1157
|
+
return false;
|
|
1158
|
+
const entryPoints = [pkg.packageJson.main, pkg.packageJson.types, pkg.packageJson.svelte, pkg.packageJson.bin, pkg.packageJson.exports];
|
|
1159
|
+
const namesPath = (value) => {
|
|
1160
|
+
const targetPath = z.string().safeParse(value);
|
|
1161
|
+
if (targetPath.success) {
|
|
1162
|
+
const target = targetPath.data.replace(/^\.\//, '');
|
|
1163
|
+
const pattern = target.split('*').map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('.*');
|
|
1164
|
+
return new RegExp('^' + pattern + '$').test(relative);
|
|
1165
|
+
}
|
|
1166
|
+
const alternatives = z.array(JsonValueSchema).safeParse(value);
|
|
1167
|
+
if (alternatives.success)
|
|
1168
|
+
return alternatives.data.some(namesPath);
|
|
1169
|
+
const conditions = z.record(z.string(), JsonValueSchema).safeParse(value);
|
|
1170
|
+
return conditions.success && Object.values(conditions.data).some(namesPath);
|
|
1171
|
+
};
|
|
1172
|
+
return !entryPoints.some(namesPath);
|
|
1173
|
+
}
|
|
1174
|
+
async function workspaceDependencyEdges(host, pkg, workspacePackages) {
|
|
1175
|
+
const workspacePackageNames = new Set(workspacePackages.keys());
|
|
1176
|
+
const declared = new Set();
|
|
1177
|
+
const declaredExternal = new Set();
|
|
1178
|
+
const optionalPeers = new Set();
|
|
1179
|
+
for (const [name, selector] of Object.entries(pkg.packageJson.dependencies ?? {})) {
|
|
1180
|
+
const target = workspacePackages.get(name);
|
|
1181
|
+
if (target && selectorResolvesWorkspacePackage(target, selector ?? ''))
|
|
1182
|
+
declared.add(name);
|
|
1183
|
+
else if (target)
|
|
1184
|
+
declaredExternal.add(name);
|
|
1185
|
+
else if (selector?.startsWith('workspace:')) {
|
|
1186
|
+
throw new Error(`Workspace artifact build plan cannot resolve ${name}, required by ${pkg.name}, to a workspace package manifest`);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
// Optional peers (peerDependenciesMeta[name].optional) are not build
|
|
1190
|
+
// prerequisites — they may be absent at runtime by contract, and treating
|
|
1191
|
+
// them as edges pulls whole optional families into every consumer's plan
|
|
1192
|
+
// and can invert cycle ordering (ai <-> ai-provider-*). Statically-imported
|
|
1193
|
+
// peers are still covered by the source scan below.
|
|
1194
|
+
for (const [name, selector] of Object.entries(pkg.packageJson.peerDependencies ?? {})) {
|
|
1195
|
+
const target = workspacePackages.get(name);
|
|
1196
|
+
// An optional peer is never part of this package's required closure,
|
|
1197
|
+
// whether or not its selector resolves the workspace version: a source
|
|
1198
|
+
// import of it orders the build only when something else already
|
|
1199
|
+
// requires it, and never drags it (or its own cycle) into the plan.
|
|
1200
|
+
if (pkg.packageJson.peerDependenciesMeta?.[name]?.optional === true) {
|
|
1201
|
+
if (target) {
|
|
1202
|
+
declaredExternal.add(name);
|
|
1203
|
+
if (selectorResolvesWorkspacePackage(target, selector ?? ''))
|
|
1204
|
+
optionalPeers.add(name);
|
|
1205
|
+
}
|
|
1206
|
+
continue;
|
|
1207
|
+
}
|
|
1208
|
+
if (target && selectorResolvesWorkspacePackage(target, selector ?? ''))
|
|
1209
|
+
declared.add(name);
|
|
1210
|
+
else if (target)
|
|
1211
|
+
declaredExternal.add(name);
|
|
1212
|
+
else if (selector?.startsWith('workspace:')) {
|
|
1213
|
+
throw new Error(`Workspace artifact build plan cannot resolve ${name}, required by ${pkg.name}, to a workspace package manifest`);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
const discovered = new Set();
|
|
1217
|
+
const optionalInputs = new Set();
|
|
1218
|
+
for (const path of await packageBuildInputs(host, pkg)) {
|
|
1219
|
+
const dynamicDiscovered = new Set();
|
|
1220
|
+
await collectImportsFromPath(host, path, discovered, workspacePackageNames, dynamicDiscovered);
|
|
1221
|
+
for (const name of dynamicDiscovered) {
|
|
1222
|
+
if (name === pkg.name)
|
|
1223
|
+
continue;
|
|
1224
|
+
const optionalPeer = pkg.packageJson.peerDependencies?.[name] !== undefined &&
|
|
1225
|
+
pkg.packageJson.peerDependenciesMeta?.[name]?.optional === true;
|
|
1226
|
+
if (optionalPeer) {
|
|
1227
|
+
if (optionalPeers.has(name))
|
|
1228
|
+
optionalInputs.add(name);
|
|
1229
|
+
continue;
|
|
1230
|
+
}
|
|
1231
|
+
if (declaredExternal.has(name))
|
|
1232
|
+
continue;
|
|
1233
|
+
const developmentSelector = isBuildOnlyImportSource(host, pkg, path)
|
|
1234
|
+
? pkg.packageJson.devDependencies?.[name]
|
|
1235
|
+
: undefined;
|
|
1236
|
+
const requiredDeclaration = pkg.packageJson.dependencies?.[name] !== undefined ||
|
|
1237
|
+
(pkg.packageJson.peerDependencies?.[name] !== undefined && !optionalPeer) ||
|
|
1238
|
+
developmentSelector !== undefined;
|
|
1239
|
+
if (!requiredDeclaration) {
|
|
1240
|
+
throw new Error(`Workspace artifact build plan found undeclared dynamic import ${name}, required by ${pkg.name}; declare it as a dependency, required peer, or optional peer (build-only imports may use a development dependency)`);
|
|
1241
|
+
}
|
|
1242
|
+
const target = workspacePackages.get(name);
|
|
1243
|
+
if (developmentSelector !== undefined && target && !selectorResolvesWorkspacePackage(target, developmentSelector)) {
|
|
1244
|
+
continue;
|
|
1245
|
+
}
|
|
1246
|
+
discovered.add(name);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
for (const name of optionalPeers) {
|
|
1250
|
+
if (discovered.has(name))
|
|
1251
|
+
optionalInputs.add(name);
|
|
1252
|
+
}
|
|
1253
|
+
for (const name of declaredExternal) {
|
|
1254
|
+
if (!declared.has(name))
|
|
1255
|
+
discovered.delete(name);
|
|
1256
|
+
}
|
|
1257
|
+
declared.delete(pkg.name);
|
|
1258
|
+
discovered.delete(pkg.name);
|
|
1259
|
+
const all = new Set([...declared, ...discovered]);
|
|
1260
|
+
return {
|
|
1261
|
+
declared: [...declared].sort(),
|
|
1262
|
+
discovered: [...discovered].sort(),
|
|
1263
|
+
all: [...all].sort(),
|
|
1264
|
+
inputs: [...new Set([...all, ...optionalInputs])].sort()
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
function shouldScanFile(path) {
|
|
1268
|
+
const filename = path.split('/').pop() ?? '';
|
|
1269
|
+
if (filename.includes('.test.') || filename.includes('.spec.') || filename.endsWith('.d.ts')) {
|
|
1270
|
+
return false;
|
|
1271
|
+
}
|
|
1272
|
+
const dot = filename.lastIndexOf('.');
|
|
1273
|
+
return dot >= 0 && SOURCE_EXTENSIONS.has(filename.slice(dot));
|
|
1274
|
+
}
|
|
1275
|
+
async function collectImportsFromPath(host, path, out, workspacePackageNames, dynamicOut) {
|
|
1276
|
+
if (!(await host.files.exists(path)))
|
|
1277
|
+
return;
|
|
1278
|
+
const stats = await followedStat(host, path);
|
|
1279
|
+
if (stats.kind === 'directory') {
|
|
1280
|
+
for (const entry of await host.files.list(path)) {
|
|
1281
|
+
if (entry.kind === 'directory' && SKIP_DIRS.has(entry.name))
|
|
1282
|
+
continue;
|
|
1283
|
+
await collectImportsFromPath(host, host.paths.join(path, entry.name), out, workspacePackageNames, dynamicOut);
|
|
1284
|
+
}
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
if (stats.kind !== 'file' || !shouldScanFile(path))
|
|
1288
|
+
return;
|
|
1289
|
+
const read = await settle(async () => readText(host.files, path));
|
|
1290
|
+
if (!read.ok) {
|
|
1291
|
+
// The file vanished between the directory walk and the read: nothing to scan.
|
|
1292
|
+
if (isMissingPathError(read.cause))
|
|
1293
|
+
return;
|
|
1294
|
+
throw read.cause;
|
|
1295
|
+
}
|
|
1296
|
+
const text = read.value;
|
|
1297
|
+
const filename = path.split('/').pop() ?? '';
|
|
1298
|
+
const dot = filename.lastIndexOf('.');
|
|
1299
|
+
const extension = dot >= 0 ? filename.slice(dot) : '';
|
|
1300
|
+
if (extension === '.css') {
|
|
1301
|
+
collectCssImports(text, out, workspacePackageNames);
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
if (extension === '.svelte') {
|
|
1305
|
+
for (const script of extractSvelteBlocks(text, 'script')) {
|
|
1306
|
+
collectEcmaScriptImports(script, out, workspacePackageNames, dynamicOut);
|
|
1307
|
+
}
|
|
1308
|
+
for (const style of extractSvelteBlocks(text, 'style')) {
|
|
1309
|
+
collectCssImports(style, out, workspacePackageNames);
|
|
1310
|
+
}
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
collectEcmaScriptImports(text, out, workspacePackageNames, dynamicOut);
|
|
1314
|
+
if (extension === '.js' || extension === '.mjs' || extension === '.cjs') {
|
|
1315
|
+
collectCssImports(text, out, workspacePackageNames);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
function collectCssImports(text, out, workspacePackageNames) {
|
|
1319
|
+
for (const match of text.matchAll(CSS_IMPORT_RE)) {
|
|
1320
|
+
if (match[1])
|
|
1321
|
+
addPackageFromSpecifier(out, match[1], workspacePackageNames);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
function extractSvelteBlocks(text, tag) {
|
|
1325
|
+
const blocks = [];
|
|
1326
|
+
const pattern = new RegExp(`<${tag}\\b[^>]*>([\\s\\S]*?)<\\/${tag}>`, 'gi');
|
|
1327
|
+
for (const match of text.matchAll(pattern)) {
|
|
1328
|
+
blocks.push(match[1] ?? '');
|
|
1329
|
+
}
|
|
1330
|
+
return blocks;
|
|
1331
|
+
}
|
|
1332
|
+
function collectEcmaScriptImports(text, out, workspacePackageNames, dynamicOut) {
|
|
1333
|
+
let index = 0;
|
|
1334
|
+
while (index < text.length) {
|
|
1335
|
+
const skipped = skipWhitespaceAndComments(text, index);
|
|
1336
|
+
index = skipped.index;
|
|
1337
|
+
if (skipped.stoppedInString) {
|
|
1338
|
+
index = skipStringLike(text, index);
|
|
1339
|
+
continue;
|
|
1340
|
+
}
|
|
1341
|
+
if (matchesKeyword(text, index, 'import')) {
|
|
1342
|
+
index = collectImportStatement(text, index + 'import'.length, out, workspacePackageNames, dynamicOut);
|
|
1343
|
+
continue;
|
|
1344
|
+
}
|
|
1345
|
+
if (matchesKeyword(text, index, 'export')) {
|
|
1346
|
+
index = collectExportStatement(text, index + 'export'.length, out, workspacePackageNames);
|
|
1347
|
+
continue;
|
|
1348
|
+
}
|
|
1349
|
+
index += 1;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
function collectImportStatement(text, index, out, workspacePackageNames, dynamicOut) {
|
|
1353
|
+
let cursor = skipWhitespaceAndComments(text, index).index;
|
|
1354
|
+
if (text[cursor] === '(') {
|
|
1355
|
+
cursor = skipWhitespaceAndComments(text, cursor + 1).index;
|
|
1356
|
+
const literal = readStringLiteral(text, cursor);
|
|
1357
|
+
if (literal) {
|
|
1358
|
+
addPackageFromSpecifier(dynamicOut ?? out, literal.value, workspacePackageNames);
|
|
1359
|
+
}
|
|
1360
|
+
return literal?.end ?? cursor + 1;
|
|
1361
|
+
}
|
|
1362
|
+
if (text[cursor] === '.')
|
|
1363
|
+
return cursor + 1;
|
|
1364
|
+
const sideEffectImport = readStringLiteral(text, cursor);
|
|
1365
|
+
if (sideEffectImport) {
|
|
1366
|
+
addPackageFromSpecifier(out, sideEffectImport.value, workspacePackageNames);
|
|
1367
|
+
return sideEffectImport.end;
|
|
1368
|
+
}
|
|
1369
|
+
return collectFromClause(text, cursor, out, workspacePackageNames);
|
|
1370
|
+
}
|
|
1371
|
+
function collectExportStatement(text, index, out, workspacePackageNames) {
|
|
1372
|
+
const cursor = skipWhitespaceAndComments(text, index).index;
|
|
1373
|
+
if (text[cursor] === '*' ||
|
|
1374
|
+
text[cursor] === '{' ||
|
|
1375
|
+
matchesKeyword(text, cursor, 'type')) {
|
|
1376
|
+
return collectFromClause(text, cursor, out, workspacePackageNames);
|
|
1377
|
+
}
|
|
1378
|
+
// `export function`, `export const`, and `export default` may contain a
|
|
1379
|
+
// dynamic import in their initializer/body. Keep scanning their contents
|
|
1380
|
+
// instead of consuming the whole declaration while looking for a `from`.
|
|
1381
|
+
return cursor;
|
|
1382
|
+
}
|
|
1383
|
+
function collectFromClause(text, index, out, workspacePackageNames) {
|
|
1384
|
+
let cursor = index;
|
|
1385
|
+
while (cursor < text.length) {
|
|
1386
|
+
const skipped = skipWhitespaceAndComments(text, cursor);
|
|
1387
|
+
cursor = skipped.index;
|
|
1388
|
+
if (skipped.stoppedInString) {
|
|
1389
|
+
cursor = skipStringLike(text, cursor);
|
|
1390
|
+
continue;
|
|
1391
|
+
}
|
|
1392
|
+
if (text[cursor] === ';')
|
|
1393
|
+
return cursor + 1;
|
|
1394
|
+
if (matchesKeyword(text, cursor, 'from')) {
|
|
1395
|
+
cursor = skipWhitespaceAndComments(text, cursor + 'from'.length).index;
|
|
1396
|
+
const literal = readStringLiteral(text, cursor);
|
|
1397
|
+
if (literal) {
|
|
1398
|
+
addPackageFromSpecifier(out, literal.value, workspacePackageNames);
|
|
1399
|
+
return literal.end;
|
|
1400
|
+
}
|
|
1401
|
+
return cursor;
|
|
1402
|
+
}
|
|
1403
|
+
cursor += 1;
|
|
1404
|
+
}
|
|
1405
|
+
return cursor;
|
|
1406
|
+
}
|
|
1407
|
+
function addPackageFromSpecifier(out, specifier, workspacePackageNames) {
|
|
1408
|
+
if (workspacePackageNames) {
|
|
1409
|
+
const exactWorkspacePackage = [...workspacePackageNames]
|
|
1410
|
+
.filter((name) => specifier === name || specifier.startsWith(`${name}/`))
|
|
1411
|
+
.sort((a, b) => b.length - a.length || a.localeCompare(b))[0];
|
|
1412
|
+
if (exactWorkspacePackage) {
|
|
1413
|
+
out.add(exactWorkspacePackage);
|
|
1414
|
+
}
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1417
|
+
const match = specifier.match(/^(@vibesdotdev\/[a-zA-Z0-9-]+)(?:\/.*)?$/);
|
|
1418
|
+
if (match?.[1])
|
|
1419
|
+
out.add(match[1]);
|
|
1420
|
+
}
|
|
1421
|
+
function matchesKeyword(text, index, keyword) {
|
|
1422
|
+
if (!text.startsWith(keyword, index))
|
|
1423
|
+
return false;
|
|
1424
|
+
const before = index > 0 ? text[index - 1] : '';
|
|
1425
|
+
const after = text[index + keyword.length] ?? '';
|
|
1426
|
+
return !isIdentifierChar(before) && !isIdentifierChar(after);
|
|
1427
|
+
}
|
|
1428
|
+
function isIdentifierChar(char) {
|
|
1429
|
+
return /[A-Za-z0-9_$]/.test(char);
|
|
1430
|
+
}
|
|
1431
|
+
function skipWhitespaceAndComments(text, index) {
|
|
1432
|
+
let cursor = index;
|
|
1433
|
+
while (cursor < text.length) {
|
|
1434
|
+
const char = text[cursor];
|
|
1435
|
+
const next = text[cursor + 1];
|
|
1436
|
+
if (/\s/.test(char)) {
|
|
1437
|
+
cursor += 1;
|
|
1438
|
+
continue;
|
|
1439
|
+
}
|
|
1440
|
+
if (char === '/' && next === '/') {
|
|
1441
|
+
const lineEnd = text.indexOf('\n', cursor + 2);
|
|
1442
|
+
cursor = lineEnd >= 0 ? lineEnd + 1 : text.length;
|
|
1443
|
+
continue;
|
|
1444
|
+
}
|
|
1445
|
+
if (char === '/' && next === '*') {
|
|
1446
|
+
const commentEnd = text.indexOf('*/', cursor + 2);
|
|
1447
|
+
cursor = commentEnd >= 0 ? commentEnd + 2 : text.length;
|
|
1448
|
+
continue;
|
|
1449
|
+
}
|
|
1450
|
+
return { index: cursor, stoppedInString: char === '"' || char === "'" || char === '`' };
|
|
1451
|
+
}
|
|
1452
|
+
return { index: cursor, stoppedInString: false };
|
|
1453
|
+
}
|
|
1454
|
+
function skipStringLike(text, index) {
|
|
1455
|
+
const quote = text[index];
|
|
1456
|
+
let cursor = index + 1;
|
|
1457
|
+
while (cursor < text.length) {
|
|
1458
|
+
const char = text[cursor];
|
|
1459
|
+
if (char === '\\') {
|
|
1460
|
+
cursor += 2;
|
|
1461
|
+
continue;
|
|
1462
|
+
}
|
|
1463
|
+
if (char === quote)
|
|
1464
|
+
return cursor + 1;
|
|
1465
|
+
cursor += 1;
|
|
1466
|
+
}
|
|
1467
|
+
return cursor;
|
|
1468
|
+
}
|
|
1469
|
+
function readStringLiteral(text, index) {
|
|
1470
|
+
const quote = text[index];
|
|
1471
|
+
if (quote !== '"' && quote !== "'")
|
|
1472
|
+
return null;
|
|
1473
|
+
let cursor = index + 1;
|
|
1474
|
+
let value = '';
|
|
1475
|
+
while (cursor < text.length) {
|
|
1476
|
+
const char = text[cursor];
|
|
1477
|
+
if (char === '\\') {
|
|
1478
|
+
value += text[cursor + 1] ?? '';
|
|
1479
|
+
cursor += 2;
|
|
1480
|
+
continue;
|
|
1481
|
+
}
|
|
1482
|
+
if (char === quote)
|
|
1483
|
+
return { value, end: cursor + 1 };
|
|
1484
|
+
value += char;
|
|
1485
|
+
cursor += 1;
|
|
1486
|
+
}
|
|
1487
|
+
return null;
|
|
1488
|
+
}
|
|
1489
|
+
function appScanPaths(host, appDir) {
|
|
1490
|
+
return [
|
|
1491
|
+
host.paths.join(appDir, 'src'),
|
|
1492
|
+
host.paths.join(appDir, 'vibes.config.js'),
|
|
1493
|
+
host.paths.join(appDir, 'vibes.config.ts'),
|
|
1494
|
+
host.paths.join(appDir, 'deployment.config.js'),
|
|
1495
|
+
host.paths.join(appDir, 'deployment.config.ts'),
|
|
1496
|
+
host.paths.join(appDir, 'svelte.config.js'),
|
|
1497
|
+
host.paths.join(appDir, 'svelte.config.ts'),
|
|
1498
|
+
host.paths.join(appDir, 'vite.config.js'),
|
|
1499
|
+
host.paths.join(appDir, 'vite.config.ts')
|
|
1500
|
+
];
|
|
1501
|
+
}
|
|
1502
|
+
function cachedEdges(cache) {
|
|
1503
|
+
return (pkg) => {
|
|
1504
|
+
const deps = cache.get(pkg.name);
|
|
1505
|
+
if (!deps) {
|
|
1506
|
+
throw new Error(`Workspace dependency edges were not loaded for ${pkg.name}`);
|
|
1507
|
+
}
|
|
1508
|
+
return deps;
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1511
|
+
async function edgesOf(host, pkg, packages, cache) {
|
|
1512
|
+
const cached = cache.get(pkg.name);
|
|
1513
|
+
if (cached)
|
|
1514
|
+
return cached;
|
|
1515
|
+
const deps = await workspaceDependencyEdges(host, pkg, packages);
|
|
1516
|
+
cache.set(pkg.name, deps);
|
|
1517
|
+
return deps;
|
|
1518
|
+
}
|
|
1519
|
+
/**
|
|
1520
|
+
* Computes the complete, dependency-ordered list of workspace packages whose
|
|
1521
|
+
* dist artifacts must exist before the app can build. Pure planning step —
|
|
1522
|
+
* does not run any builds.
|
|
1523
|
+
*
|
|
1524
|
+
* Edges are the union of declared workspace deps (dependencies +
|
|
1525
|
+
* peerDependencies) and source-scanned imports, so transitive deps reachable
|
|
1526
|
+
* only through peerDependencies or undeclared imports are included. Cycles in
|
|
1527
|
+
* that union (e.g. runtime <-> runtime-client via source imports) are
|
|
1528
|
+
* tolerated: a back-edge to a package already on the visit stack is skipped,
|
|
1529
|
+
* which still yields a valid topological order for the acyclic remainder.
|
|
1530
|
+
*/
|
|
1531
|
+
export async function planWorkspaceArtifactBuilds(options) {
|
|
1532
|
+
const host = await resolveArtifactHost(options.runtime);
|
|
1533
|
+
return (await planWorkspacePackageClosure(host, options)).filter(hasBuildArtifacts);
|
|
1534
|
+
}
|
|
1535
|
+
/** Closure from explicit package seeds (publish preflight) — same transitive walk as the app path. */
|
|
1536
|
+
export async function planWorkspacePackageClosureFromSeeds(runtime, workspaceRoot, seedNames, packageScope) {
|
|
1537
|
+
const host = await resolveArtifactHost(runtime);
|
|
1538
|
+
const packages = await resolveWorkspacePackages(host, workspaceRoot, packageScope);
|
|
1539
|
+
const dependencyCache = new Map();
|
|
1540
|
+
const planned = new Set();
|
|
1541
|
+
const visiting = new Set();
|
|
1542
|
+
const order = [];
|
|
1543
|
+
const visit = async (pkg) => {
|
|
1544
|
+
if (planned.has(pkg.name) || visiting.has(pkg.name))
|
|
1545
|
+
return;
|
|
1546
|
+
visiting.add(pkg.name);
|
|
1547
|
+
for (const depName of (await edgesOf(host, pkg, packages, dependencyCache)).all) {
|
|
1548
|
+
const dep = packages.get(depName);
|
|
1549
|
+
if (!dep) {
|
|
1550
|
+
throw new Error(`Workspace package closure cannot resolve ${depName}, required by ${pkg.name}, to a workspace package manifest`);
|
|
1551
|
+
}
|
|
1552
|
+
await visit(dep);
|
|
1553
|
+
}
|
|
1554
|
+
visiting.delete(pkg.name);
|
|
1555
|
+
planned.add(pkg.name);
|
|
1556
|
+
order.push(pkg);
|
|
1557
|
+
};
|
|
1558
|
+
for (const name of [...seedNames].sort()) {
|
|
1559
|
+
const pkg = packages.get(name);
|
|
1560
|
+
if (!pkg) {
|
|
1561
|
+
throw new Error(`Workspace package closure seed ${name} is not a workspace package`);
|
|
1562
|
+
}
|
|
1563
|
+
await visit(pkg);
|
|
1564
|
+
}
|
|
1565
|
+
return orderCycleMembersByWorkspaceDependencies(order, cachedEdges(dependencyCache));
|
|
1566
|
+
}
|
|
1567
|
+
async function workspaceAppDependencyNames(host, workspaceRoot, appDir, packages) {
|
|
1568
|
+
const workspacePackageNames = new Set(packages.keys());
|
|
1569
|
+
const appImports = new Set();
|
|
1570
|
+
for (const path of appScanPaths(host, appDir)) {
|
|
1571
|
+
await collectImportsFromPath(host, path, appImports, workspacePackageNames);
|
|
1572
|
+
}
|
|
1573
|
+
const appPackageJson = await readAppManifest(host, host.paths.join(appDir, 'package.json'));
|
|
1574
|
+
const localDeclared = new Set();
|
|
1575
|
+
const declaredExternal = new Set();
|
|
1576
|
+
for (const [name, selector] of Object.entries(appPackageJson?.dependencies ?? {})) {
|
|
1577
|
+
const target = packages.get(name);
|
|
1578
|
+
if (target && selectorResolvesWorkspacePackage(target, selector ?? '')) {
|
|
1579
|
+
appImports.add(name);
|
|
1580
|
+
localDeclared.add(name);
|
|
1581
|
+
}
|
|
1582
|
+
else if (target)
|
|
1583
|
+
declaredExternal.add(name);
|
|
1584
|
+
else if (selector?.startsWith('workspace:')) {
|
|
1585
|
+
throw new Error(`Workspace artifact build plan cannot resolve ${name}, required by ${host.paths.relative(workspaceRoot, appDir)}, to a workspace package manifest`);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
for (const [name, selector] of Object.entries(appPackageJson?.peerDependencies ?? {})) {
|
|
1589
|
+
const target = packages.get(name);
|
|
1590
|
+
if (appPackageJson?.peerDependenciesMeta?.[name]?.optional === true) {
|
|
1591
|
+
if (target && !selectorResolvesWorkspacePackage(target, selector ?? '')) {
|
|
1592
|
+
declaredExternal.add(name);
|
|
1593
|
+
}
|
|
1594
|
+
continue;
|
|
1595
|
+
}
|
|
1596
|
+
if (target && selectorResolvesWorkspacePackage(target, selector ?? '')) {
|
|
1597
|
+
appImports.add(name);
|
|
1598
|
+
localDeclared.add(name);
|
|
1599
|
+
}
|
|
1600
|
+
else if (target)
|
|
1601
|
+
declaredExternal.add(name);
|
|
1602
|
+
else if (selector?.startsWith('workspace:')) {
|
|
1603
|
+
throw new Error(`Workspace artifact build plan cannot resolve ${name}, required by ${host.paths.relative(workspaceRoot, appDir)}, to a workspace package manifest`);
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
for (const name of declaredExternal) {
|
|
1607
|
+
if (!localDeclared.has(name))
|
|
1608
|
+
appImports.delete(name);
|
|
1609
|
+
}
|
|
1610
|
+
return [...appImports].sort();
|
|
1611
|
+
}
|
|
1612
|
+
async function planWorkspacePackageClosure(host, { workspaceRoot, appDir }) {
|
|
1613
|
+
const packages = await discoverWorkspacePackages(host, workspaceRoot);
|
|
1614
|
+
const appImports = await workspaceAppDependencyNames(host, workspaceRoot, appDir, packages);
|
|
1615
|
+
const dependencyCache = new Map();
|
|
1616
|
+
const planned = new Set();
|
|
1617
|
+
const visiting = new Set();
|
|
1618
|
+
const order = [];
|
|
1619
|
+
const visit = async (pkg) => {
|
|
1620
|
+
// A package already on the visit stack is a dependency cycle; skip the
|
|
1621
|
+
// back-edge instead of failing — every member still gets built once.
|
|
1622
|
+
if (planned.has(pkg.name) || visiting.has(pkg.name))
|
|
1623
|
+
return;
|
|
1624
|
+
visiting.add(pkg.name);
|
|
1625
|
+
for (const depName of (await edgesOf(host, pkg, packages, dependencyCache)).all) {
|
|
1626
|
+
const dep = packages.get(depName);
|
|
1627
|
+
if (!dep) {
|
|
1628
|
+
throw new Error(`Workspace artifact build plan cannot resolve ${depName}, required by ${pkg.name}, to a workspace package manifest`);
|
|
1629
|
+
}
|
|
1630
|
+
await visit(dep);
|
|
1631
|
+
}
|
|
1632
|
+
visiting.delete(pkg.name);
|
|
1633
|
+
planned.add(pkg.name);
|
|
1634
|
+
order.push(pkg);
|
|
1635
|
+
};
|
|
1636
|
+
for (const name of appImports) {
|
|
1637
|
+
const pkg = packages.get(name);
|
|
1638
|
+
if (!pkg)
|
|
1639
|
+
continue;
|
|
1640
|
+
await visit(pkg);
|
|
1641
|
+
}
|
|
1642
|
+
return orderCycleMembersByWorkspaceDependencies(order, cachedEdges(dependencyCache));
|
|
1643
|
+
}
|
|
1644
|
+
/**
|
|
1645
|
+
* Refuses a deploy before its ledger, regeneration, build, or provider phases
|
|
1646
|
+
* when the dependency closure could resolve outside the requested worktree or
|
|
1647
|
+
* consume stale Node-condition artifacts. This deliberately has no skip-build
|
|
1648
|
+
* branch: skipping a rebuild must not permit unverified deploy inputs.
|
|
1649
|
+
*/
|
|
1650
|
+
export async function assertDeployWorkspacePreflight({ workspaceRoot, runtime, appDir, packageNames, packageScope, artifactPolicy = 'strict' }) {
|
|
1651
|
+
const host = await resolveArtifactHost(runtime);
|
|
1652
|
+
const resolvedWorkspaceRoot = await realpathChecked(host.files, workspaceRoot);
|
|
1653
|
+
const nodeModules = host.paths.join(resolvedWorkspaceRoot, 'node_modules');
|
|
1654
|
+
if (!(await host.files.exists(nodeModules))) {
|
|
1655
|
+
throw deployPreflightFailure(`the deploy worktree has no node_modules at ${nodeModules}. Run \`bun install\` in ${resolvedWorkspaceRoot}.`);
|
|
1656
|
+
}
|
|
1657
|
+
const resolvedNodeModules = await realpathChecked(host.files, nodeModules);
|
|
1658
|
+
if (!isPathWithin(host, resolvedWorkspaceRoot, resolvedNodeModules)) {
|
|
1659
|
+
throw deployPreflightFailure(`node_modules resolves outside the deploy worktree (${resolvedNodeModules}). Run \`bun install\` in ${resolvedWorkspaceRoot}; do not reuse another worktree's dependencies.`);
|
|
1660
|
+
}
|
|
1661
|
+
const warnings = [];
|
|
1662
|
+
if ((appDir === undefined) === (packageNames === undefined)) {
|
|
1663
|
+
throw deployPreflightFailure('workspace preflight needs exactly one of appDir or packageNames as its closure seed');
|
|
1664
|
+
}
|
|
1665
|
+
const workspacePackages = appDir !== undefined
|
|
1666
|
+
? await discoverWorkspacePackages(host, resolvedWorkspaceRoot)
|
|
1667
|
+
: await resolveWorkspacePackages(host, resolvedWorkspaceRoot, packageScope);
|
|
1668
|
+
let packageClosure;
|
|
1669
|
+
if (appDir !== undefined) {
|
|
1670
|
+
const resolvedAppDir = await realpathChecked(host.files, appDir);
|
|
1671
|
+
if (!isPathWithin(host, resolvedWorkspaceRoot, resolvedAppDir)) {
|
|
1672
|
+
throw deployPreflightFailure(`app resolves outside the deploy worktree (${resolvedAppDir}). Run \`bun install\` in ${resolvedWorkspaceRoot}.`);
|
|
1673
|
+
}
|
|
1674
|
+
packageClosure = await planWorkspacePackageClosure(host, {
|
|
1675
|
+
workspaceRoot: resolvedWorkspaceRoot,
|
|
1676
|
+
appDir: resolvedAppDir
|
|
1677
|
+
});
|
|
1678
|
+
for (const dependencyName of await workspaceAppDependencyNames(host, resolvedWorkspaceRoot, resolvedAppDir, workspacePackages)) {
|
|
1679
|
+
await assertInstalledWorkspacePackageResolution(host, {
|
|
1680
|
+
consumerDir: resolvedAppDir,
|
|
1681
|
+
consumerLabel: host.paths.relative(resolvedWorkspaceRoot, resolvedAppDir),
|
|
1682
|
+
dependencyName,
|
|
1683
|
+
workspaceRoot: resolvedWorkspaceRoot
|
|
1684
|
+
});
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
else if (packageNames !== undefined) {
|
|
1688
|
+
packageClosure = selectWorkspacePackagesByName(workspacePackages, packageNames);
|
|
1689
|
+
}
|
|
1690
|
+
else {
|
|
1691
|
+
throw deployPreflightFailure('workspace preflight needs exactly one of appDir or packageNames as its closure seed');
|
|
1692
|
+
}
|
|
1693
|
+
for (const pkg of packageClosure) {
|
|
1694
|
+
const resolvedPackageRoot = await realpathChecked(host.files, pkg.dir);
|
|
1695
|
+
if (!isPathWithin(host, resolvedWorkspaceRoot, resolvedPackageRoot)) {
|
|
1696
|
+
throw deployPreflightFailure(`${pkg.name} resolves outside the deploy worktree (${resolvedPackageRoot}). Run \`bun install\` in ${resolvedWorkspaceRoot}.`);
|
|
1697
|
+
}
|
|
1698
|
+
for (const dependencyName of (await workspaceDependencyEdges(host, pkg, workspacePackages)).all) {
|
|
1699
|
+
await assertInstalledWorkspacePackageResolution(host, {
|
|
1700
|
+
consumerDir: resolvedPackageRoot,
|
|
1701
|
+
consumerLabel: pkg.name,
|
|
1702
|
+
dependencyName,
|
|
1703
|
+
workspaceRoot: resolvedWorkspaceRoot
|
|
1704
|
+
});
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
if (artifactPolicy === 'planned-build') {
|
|
1708
|
+
return { warnings };
|
|
1709
|
+
}
|
|
1710
|
+
const ownHashes = new Map();
|
|
1711
|
+
for (const pkg of packageClosure.filter(hasBuildArtifacts)) {
|
|
1712
|
+
// Single source of truth: the same predicate that decides whether the
|
|
1713
|
+
// build orchestration below may skip this package's build (see
|
|
1714
|
+
// `checkWorkspacePackageFreshness`) decides whether this postflight
|
|
1715
|
+
// assertion accepts it as a valid deploy input.
|
|
1716
|
+
const freshness = await checkWorkspacePackageFreshness(host, pkg, workspacePackages, ownHashes);
|
|
1717
|
+
if (freshness.fresh)
|
|
1718
|
+
continue;
|
|
1719
|
+
if (freshness.artifactsMissing) {
|
|
1720
|
+
throw deployPreflightFailure(`${pkg.name} has no ${freshness.artifactsDir} artifacts. ${staleArtifactRemedy(freshness.artifactsDir)}`);
|
|
1721
|
+
}
|
|
1722
|
+
throw deployPreflightFailure(`${pkg.name} ${freshness.artifactsDir} is stale for this deploy: ${freshness.reason}. ${staleArtifactRemedy(freshness.artifactsDir)}`);
|
|
1723
|
+
}
|
|
1724
|
+
return { warnings };
|
|
1725
|
+
}
|
|
1726
|
+
function selectWorkspacePackagesByName(packages, packageNames) {
|
|
1727
|
+
const selected = [];
|
|
1728
|
+
const seen = new Set();
|
|
1729
|
+
for (const name of packageNames) {
|
|
1730
|
+
if (seen.has(name)) {
|
|
1731
|
+
throw new Error(`Workspace preflight package closure repeats ${name}`);
|
|
1732
|
+
}
|
|
1733
|
+
seen.add(name);
|
|
1734
|
+
const pkg = packages.get(name);
|
|
1735
|
+
if (!pkg) {
|
|
1736
|
+
throw new Error(`Workspace preflight package ${name} is not a workspace package`);
|
|
1737
|
+
}
|
|
1738
|
+
selected.push(pkg);
|
|
1739
|
+
}
|
|
1740
|
+
return selected;
|
|
1741
|
+
}
|
|
1742
|
+
/**
|
|
1743
|
+
* Orders one caller-owned package set by the dependencies its production
|
|
1744
|
+
* source actually imports. This does not expand the set: Registry owns the
|
|
1745
|
+
* release closure, while this planner owns the artifact order inside it.
|
|
1746
|
+
* A selected package importing an omitted workspace package is a broken sealed
|
|
1747
|
+
* closure and fails before any build starts.
|
|
1748
|
+
*/
|
|
1749
|
+
export async function planWorkspacePackageBuildOrder(runtime, workspaceRoot, packageNames, packageScope) {
|
|
1750
|
+
const host = await resolveArtifactHost(runtime);
|
|
1751
|
+
const packages = await resolveWorkspacePackages(host, workspaceRoot, packageScope);
|
|
1752
|
+
const selected = selectWorkspacePackagesByName(packages, packageNames);
|
|
1753
|
+
const selectedNames = new Set(selected.map((pkg) => pkg.name));
|
|
1754
|
+
const dependencyCache = new Map();
|
|
1755
|
+
for (const pkg of selected) {
|
|
1756
|
+
const dependencies = await edgesOf(host, pkg, packages, dependencyCache);
|
|
1757
|
+
for (const dependency of dependencies.all) {
|
|
1758
|
+
if (!selectedNames.has(dependency)) {
|
|
1759
|
+
throw new Error(`Workspace artifact package closure omits ${dependency}, imported by ${pkg.name}`);
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
return orderCycleMembersByWorkspaceDependencies(selected, cachedEdges(dependencyCache)).map((pkg) => pkg.name);
|
|
1764
|
+
}
|
|
1765
|
+
/**
|
|
1766
|
+
* Only the workspace-artifacts builder writes the build stamp, so the remedy
|
|
1767
|
+
* names a run that goes through it; a package's own `build` script never does.
|
|
1768
|
+
*/
|
|
1769
|
+
function staleArtifactRemedy(artifactsDir) {
|
|
1770
|
+
return `Rerun without \`--skip-build\` so the workspace-artifacts build rebuilds it and records ${artifactsDir}/${BUILD_STAMP_FILENAME}; a package's own \`bun run build\` does not write that stamp.`;
|
|
1771
|
+
}
|
|
1772
|
+
function deployPreflightFailure(message) {
|
|
1773
|
+
return new Error(`[workspace-preflight] Refusing deploy: ${message}`);
|
|
1774
|
+
}
|
|
1775
|
+
function isPathWithin(host, root, target) {
|
|
1776
|
+
const pathFromRoot = host.paths.relative(root, target);
|
|
1777
|
+
return (pathFromRoot === '' ||
|
|
1778
|
+
(pathFromRoot !== '..' &&
|
|
1779
|
+
!pathFromRoot.startsWith(`..${host.sep}`) &&
|
|
1780
|
+
!host.paths.isAbsolute(pathFromRoot)));
|
|
1781
|
+
}
|
|
1782
|
+
async function resolveInstalledWorkspacePackage(host, consumerDir, workspaceRoot, packageName) {
|
|
1783
|
+
let current = await realpathChecked(host.files, consumerDir);
|
|
1784
|
+
while (isPathWithin(host, workspaceRoot, current)) {
|
|
1785
|
+
const candidate = host.paths.join(current, 'node_modules', ...packageName.split('/'));
|
|
1786
|
+
if (await host.files.exists(candidate))
|
|
1787
|
+
return candidate;
|
|
1788
|
+
if (current === workspaceRoot)
|
|
1789
|
+
break;
|
|
1790
|
+
current = host.paths.dirname(current);
|
|
1791
|
+
}
|
|
1792
|
+
return undefined;
|
|
1793
|
+
}
|
|
1794
|
+
async function assertInstalledWorkspacePackageResolution(host, options) {
|
|
1795
|
+
const installedPackage = await resolveInstalledWorkspacePackage(host, options.consumerDir, options.workspaceRoot, options.dependencyName);
|
|
1796
|
+
if (!installedPackage) {
|
|
1797
|
+
throw deployPreflightFailure(`cannot resolve ${options.dependencyName}, required by ${options.consumerLabel}, from this worktree's node_modules. Run \`bun install\` in ${options.workspaceRoot}.`);
|
|
1798
|
+
}
|
|
1799
|
+
const resolvedInstalledPackage = await realpathChecked(host.files, installedPackage);
|
|
1800
|
+
if (!isPathWithin(host, options.workspaceRoot, resolvedInstalledPackage)) {
|
|
1801
|
+
throw deployPreflightFailure(`${options.dependencyName}, required by ${options.consumerLabel}, resolves outside the deploy worktree (${resolvedInstalledPackage}). Run \`bun install\` in ${options.workspaceRoot}; do not reuse another worktree's dependencies.`);
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
/**
|
|
1805
|
+
* A file under `src/` the build reads. Hidden files are generated sentinels
|
|
1806
|
+
* (`@vibesdotdev/editor`'s nvim install writes `.vibes-install.json` through a
|
|
1807
|
+
* symlink into `editor/src/`), and tests, including whole `__tests__`
|
|
1808
|
+
* directories, are excluded from the tsconfig, so none of them is a build input.
|
|
1809
|
+
*/
|
|
1810
|
+
function isBuildInput(path) {
|
|
1811
|
+
const filename = path.split('/').pop() ?? '';
|
|
1812
|
+
if (filename.startsWith('.'))
|
|
1813
|
+
return false;
|
|
1814
|
+
if (filename.includes('.test.') || filename.includes('.spec.'))
|
|
1815
|
+
return false;
|
|
1816
|
+
return !path.includes('/__tests__/');
|
|
1817
|
+
}
|
|
1818
|
+
async function filesInDirectory(host, directory, include = () => true) {
|
|
1819
|
+
if (!(await host.files.exists(directory)))
|
|
1820
|
+
return [];
|
|
1821
|
+
const files = [];
|
|
1822
|
+
const visit = async (path) => {
|
|
1823
|
+
for (const entry of await host.files.list(path)) {
|
|
1824
|
+
const entryPath = host.paths.join(path, entry.name);
|
|
1825
|
+
if (entry.kind === 'directory') {
|
|
1826
|
+
if (!SKIP_DIRS.has(entry.name))
|
|
1827
|
+
await visit(entryPath);
|
|
1828
|
+
continue;
|
|
1829
|
+
}
|
|
1830
|
+
if (entry.kind === 'file' && include(entryPath))
|
|
1831
|
+
files.push(entryPath);
|
|
1832
|
+
}
|
|
1833
|
+
};
|
|
1834
|
+
await visit(directory);
|
|
1835
|
+
return files;
|
|
1836
|
+
}
|
|
1837
|
+
function orderCycleMembersByWorkspaceDependencies(order, dependenciesOf) {
|
|
1838
|
+
const allEdges = new Map();
|
|
1839
|
+
const packageByName = new Map(order.map((pkg) => [pkg.name, pkg]));
|
|
1840
|
+
const orderIndex = new Map(order.map((pkg, index) => [pkg.name, index]));
|
|
1841
|
+
const reachable = new Set(packageByName.keys());
|
|
1842
|
+
for (const pkg of order) {
|
|
1843
|
+
allEdges.set(pkg.name, dependenciesOf(pkg).all.filter((name) => reachable.has(name)));
|
|
1844
|
+
}
|
|
1845
|
+
const componentByName = findStronglyConnectedComponents([...reachable], allEdges);
|
|
1846
|
+
const components = new Map();
|
|
1847
|
+
for (const pkg of order) {
|
|
1848
|
+
const component = componentByName.get(pkg.name);
|
|
1849
|
+
if (component === undefined)
|
|
1850
|
+
continue;
|
|
1851
|
+
const members = components.get(component) ?? [];
|
|
1852
|
+
members.push(pkg);
|
|
1853
|
+
components.set(component, members);
|
|
1854
|
+
}
|
|
1855
|
+
const output = [];
|
|
1856
|
+
for (const component of sortComponentsByDependencies(components, componentByName, allEdges, orderIndex)) {
|
|
1857
|
+
const members = components.get(component) ?? [];
|
|
1858
|
+
if (members.length === 0)
|
|
1859
|
+
continue;
|
|
1860
|
+
if (members.length === 1) {
|
|
1861
|
+
output.push(members[0]);
|
|
1862
|
+
continue;
|
|
1863
|
+
}
|
|
1864
|
+
output.push(...sortComponentByDeclaredWorkspaceDependencies(members, dependenciesOf, orderIndex));
|
|
1865
|
+
}
|
|
1866
|
+
return output;
|
|
1867
|
+
}
|
|
1868
|
+
function sortComponentsByDependencies(components, componentByName, allEdges, orderIndex) {
|
|
1869
|
+
const componentDeps = new Map();
|
|
1870
|
+
for (const component of components.keys())
|
|
1871
|
+
componentDeps.set(component, new Set());
|
|
1872
|
+
for (const [name, deps] of allEdges) {
|
|
1873
|
+
const component = componentByName.get(name);
|
|
1874
|
+
if (component === undefined)
|
|
1875
|
+
continue;
|
|
1876
|
+
const depsForComponent = componentDeps.get(component);
|
|
1877
|
+
if (!depsForComponent)
|
|
1878
|
+
continue;
|
|
1879
|
+
for (const dep of deps) {
|
|
1880
|
+
const depComponent = componentByName.get(dep);
|
|
1881
|
+
if (depComponent === undefined || depComponent === component)
|
|
1882
|
+
continue;
|
|
1883
|
+
depsForComponent.add(depComponent);
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
const stableComponents = [...components.keys()].sort((a, b) => {
|
|
1887
|
+
const aIndex = Math.min(...(components.get(a) ?? []).map((pkg) => orderIndex.get(pkg.name) ?? Number.MAX_SAFE_INTEGER));
|
|
1888
|
+
const bIndex = Math.min(...(components.get(b) ?? []).map((pkg) => orderIndex.get(pkg.name) ?? Number.MAX_SAFE_INTEGER));
|
|
1889
|
+
return aIndex - bIndex;
|
|
1890
|
+
});
|
|
1891
|
+
const visiting = new Set();
|
|
1892
|
+
const emitted = new Set();
|
|
1893
|
+
const output = [];
|
|
1894
|
+
const visit = (component) => {
|
|
1895
|
+
if (emitted.has(component))
|
|
1896
|
+
return;
|
|
1897
|
+
if (visiting.has(component))
|
|
1898
|
+
return;
|
|
1899
|
+
visiting.add(component);
|
|
1900
|
+
const deps = [...(componentDeps.get(component) ?? [])].sort((a, b) => stableComponents.indexOf(a) - stableComponents.indexOf(b));
|
|
1901
|
+
for (const dep of deps)
|
|
1902
|
+
visit(dep);
|
|
1903
|
+
visiting.delete(component);
|
|
1904
|
+
emitted.add(component);
|
|
1905
|
+
output.push(component);
|
|
1906
|
+
};
|
|
1907
|
+
for (const component of stableComponents)
|
|
1908
|
+
visit(component);
|
|
1909
|
+
return output;
|
|
1910
|
+
}
|
|
1911
|
+
function findStronglyConnectedComponents(names, edges) {
|
|
1912
|
+
// The shared traversal primitive (`planDag`): the partition into strongly
|
|
1913
|
+
// connected components, dependencies first. Callers pass names that are
|
|
1914
|
+
// unique and edges that name only known packages (both filter first).
|
|
1915
|
+
const componentByName = new Map();
|
|
1916
|
+
planDag({ nodes: names.map((name) => ({ id: name, dependsOn: edges.get(name) ?? [] })) }).cohorts.forEach((cohort, component) => {
|
|
1917
|
+
for (const name of cohort)
|
|
1918
|
+
componentByName.set(name, component);
|
|
1919
|
+
});
|
|
1920
|
+
return componentByName;
|
|
1921
|
+
}
|
|
1922
|
+
/**
|
|
1923
|
+
* An SCC exists only because source discovery added a back-edge. Declared
|
|
1924
|
+
* dependency edges are the build contract and must still remain strict inside
|
|
1925
|
+
* that SCC: a package's TypeScript build resolves its declared workspace deps
|
|
1926
|
+
* through their dist exports. Ordering the SCC by the combined edge set lets a
|
|
1927
|
+
* discovered back-edge arbitrarily move a declared prerequisite after its
|
|
1928
|
+
* consumer (for example runtime after runtime-environment-cef).
|
|
1929
|
+
*/
|
|
1930
|
+
function sortComponentByDeclaredWorkspaceDependencies(members, dependenciesOf, orderIndex) {
|
|
1931
|
+
const memberNames = new Set(members.map((pkg) => pkg.name));
|
|
1932
|
+
const memberByName = new Map(members.map((pkg) => [pkg.name, pkg]));
|
|
1933
|
+
const sortedMembers = [...members].sort((a, b) => (orderIndex.get(a.name) ?? 0) - (orderIndex.get(b.name) ?? 0));
|
|
1934
|
+
const orderBy = (input, selectDependencies) => {
|
|
1935
|
+
const emitted = new Set();
|
|
1936
|
+
const visiting = new Set();
|
|
1937
|
+
const output = [];
|
|
1938
|
+
const visit = (pkg) => {
|
|
1939
|
+
if (emitted.has(pkg.name))
|
|
1940
|
+
return;
|
|
1941
|
+
if (visiting.has(pkg.name))
|
|
1942
|
+
return;
|
|
1943
|
+
visiting.add(pkg.name);
|
|
1944
|
+
for (const depName of selectDependencies(dependenciesOf(pkg))) {
|
|
1945
|
+
if (!memberNames.has(depName))
|
|
1946
|
+
continue;
|
|
1947
|
+
const dep = memberByName.get(depName);
|
|
1948
|
+
if (dep)
|
|
1949
|
+
visit(dep);
|
|
1950
|
+
}
|
|
1951
|
+
visiting.delete(pkg.name);
|
|
1952
|
+
emitted.add(pkg.name);
|
|
1953
|
+
output.push(pkg);
|
|
1954
|
+
};
|
|
1955
|
+
for (const pkg of input)
|
|
1956
|
+
visit(pkg);
|
|
1957
|
+
return output;
|
|
1958
|
+
};
|
|
1959
|
+
// Keep as much source-discovered ordering as the SCC permits, then enforce
|
|
1960
|
+
// the non-negotiable manifest contract over that deterministic preference.
|
|
1961
|
+
const preferred = orderBy(sortedMembers, (edges) => edges.all);
|
|
1962
|
+
const output = orderBy(preferred, (edges) => edges.declared);
|
|
1963
|
+
const outputIndex = new Map(output.map((pkg, index) => [pkg.name, index]));
|
|
1964
|
+
for (const pkg of output) {
|
|
1965
|
+
for (const dependency of dependenciesOf(pkg).declared) {
|
|
1966
|
+
if (!memberNames.has(dependency))
|
|
1967
|
+
continue;
|
|
1968
|
+
if ((outputIndex.get(dependency) ?? -1) >= (outputIndex.get(pkg.name) ?? -1)) {
|
|
1969
|
+
throw new Error(`Workspace artifact build plan contains a declared dependency cycle: ${pkg.name} requires ${dependency}`);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
return output;
|
|
1974
|
+
}
|
|
1975
|
+
export async function buildWorkspaceArtifactsForApp({ workspaceRoot, appDir, execution, runtime, onLine, dryRun = false, artifactLock }) {
|
|
1976
|
+
if (dryRun) {
|
|
1977
|
+
const plan = await planWorkspaceArtifactBuilds({ workspaceRoot, appDir, runtime });
|
|
1978
|
+
onLine?.('stdout', `[workspace-artifacts] dry-run: planned ${plan.length} package artifact build(s); no builds executed`);
|
|
1979
|
+
return;
|
|
1980
|
+
}
|
|
1981
|
+
return withWorkspaceArtifactLock({ runtime, workspaceRoot, onLine, artifactLock }, async () => {
|
|
1982
|
+
const host = await resolveArtifactHost(runtime);
|
|
1983
|
+
const plan = await planWorkspaceArtifactBuilds({ workspaceRoot, appDir, runtime });
|
|
1984
|
+
const appLabel = host.paths.relative(workspaceRoot, appDir);
|
|
1985
|
+
await syncMissingSvelteKitMetadataForWorkspace(host, workspaceRoot, appDir, execution, onLine);
|
|
1986
|
+
const packages = await discoverWorkspacePackages(host, workspaceRoot);
|
|
1987
|
+
await buildWorkspacePlanWithFreshnessSkip(host, workspaceRoot, plan, packages, execution, onLine);
|
|
1988
|
+
if (plan.length > 0) {
|
|
1989
|
+
onLine?.('stdout', `[workspace-artifacts] built ${plan.length} package artifact(s) for ${appLabel}`);
|
|
1990
|
+
}
|
|
1991
|
+
});
|
|
1992
|
+
}
|
|
1993
|
+
/**
|
|
1994
|
+
* Builds exactly the package set supplied by its owner, dependency-ordering it
|
|
1995
|
+
* from the selected packages' production source imports. Registry release
|
|
1996
|
+
* planning still owns membership; this step neither expands nor guesses it.
|
|
1997
|
+
*/
|
|
1998
|
+
export async function buildWorkspaceArtifactsForPackages({ workspaceRoot, packageNames, packageScope, execution, runtime, onLine, artifactLock }) {
|
|
1999
|
+
return withWorkspaceArtifactLock({ runtime, workspaceRoot, onLine, artifactLock }, async () => {
|
|
2000
|
+
const host = await resolveArtifactHost(runtime);
|
|
2001
|
+
const packages = await resolveWorkspacePackages(host, workspaceRoot, packageScope);
|
|
2002
|
+
const packageByName = new Map(selectWorkspacePackagesByName(packages, packageNames).map((pkg) => [pkg.name, pkg]));
|
|
2003
|
+
const plan = (await planWorkspacePackageBuildOrder(runtime, workspaceRoot, packageNames, packageScope))
|
|
2004
|
+
.map((name) => packageByName.get(name))
|
|
2005
|
+
.filter(hasBuildArtifacts);
|
|
2006
|
+
if (plan.length === 0)
|
|
2007
|
+
return;
|
|
2008
|
+
onLine?.('stdout', `[workspace-artifacts] building ${plan.length} package(s) in dependency order`);
|
|
2009
|
+
await buildWorkspacePlanWithFreshnessSkip(host, workspaceRoot, plan, packages, execution, onLine);
|
|
2010
|
+
onLine?.('stdout', `[workspace-artifacts] built ${plan.length} release package artifact(s)`);
|
|
2011
|
+
});
|
|
2012
|
+
}
|
|
2013
|
+
//# sourceMappingURL=build-workspace-artifacts.js.map
|