Skip to content

v0.15.0

Choose a tag to compare

@donalddellapietra donalddellapietra released this 06 Aug 20:52
· 39 commits to main since this release

Minor Changes

  • deepspace app migrate is removed, replaced by deepspace app update (below). It existed for one historical cutover — moving an app from a name-shaped id to a canonical one — and the platform endpoints behind it are unchanged, so an app still on a legacy id migrates with npx deepspace@0.13.0 app migrate and then upgrades normally.

    A file that no longer exists now answers 404, not the app's HTML. Deploys configured the asset layer with not_found_handling: "single-page-application", so it answered EVERY unmatched path with index.html at 200 — correct for a client route, wrong for a file. A deploy replaces the hashed build chunks, so a tab still holding the previous index.html requested one and got HTML where JavaScript belonged: the script tag parsed it, failed, and the page went white with no error in any log, monitor, or network panel. The same answer went to agents probing /llms.txt and /.well-known/mcp, telling them the app publishes a manifest it does not have.

    The asset layer cannot tell a route from a file. The app's worker can — it knows /assets/* is build output — so the decision moves there: not_found_handling: "none" makes the binding report a real miss, and the worker serves the shell for a route or 404s a file. This is what the scaffold's route handler was written for all along; the config had made every branch under its status === 404 check unreachable, which is why the reservation shipped in 0.13.0 never worked.

    That setting is now the app's to declare, and the platform honors it. It was first shipped as a platform-wide override, on the reasoning that every deployed app's catch-all already branched on a 404. The branch did exist — but it asked the asset layer for /index.html, which redirects to /. Under the old setting that branch never ran, so nothing showed; under a forced one it ran on every client route and handed the browser a 307 off the URL it asked for. An app built from the published template lost every deep link. A deploy that declares nothing keeps exactly the behavior the platform used to force, so an un-upgraded CLI cannot be broken by this release.

    deepspace app update moves an existing app onto it, and replaces deepspace migrate. An app's worker is a copy its author owns, so a platform change that needs different app code reaches nobody without a command that carries those copies forward. It pins the SDK, applies the source edits that are deterministic, and for anything it cannot safely touch prints the file, the line, and the change required — output an agent can execute. Edits still land when such a change is outstanding, because withholding them would leave the app on the old behavior for the sake of an edit someone else has to make; what is withheld is the recorded-as-done stamp, so the next run looks again. A version gap too wide to carry in one step points at the upgrade guide rather than half-migrating.

    Verified on a real deployment, not in a mock, on both sides of the change: an unmigrated create-deepspace@0.13.0 app deploys and still serves /settings at 200 with no redirect, and the same app after deepspace app update answers a missing hashed chunk with 404 application/json (including with a browser's Sec-Fetch-Dest: script), 404s /llms.txt, still serves /favicon.ico, and still serves a deep link at 200 on the URL that was asked for. A tab that outlives a deploy also now reloads itself once on Vite's vite:preloadError rather than leaving a lazily-loaded route silently dead.

    An app's compatibility_date is honored instead of silently discarded. wrangler.toml declared one runtime and the deploy hardcoded another, so local dev and production could run different semantics with nothing reporting the divergence. The declared date is now used, floored at the platform minimum (an app may move forward onto semantics it has tested, never backward onto ones the platform no longer supports) and clamped if it is in the future. The date a release ran on is recorded in its bundle, so a rollback restores that runtime rather than today's. The navigation-routing flag is now stated explicitly in the upload rather than inherited from that date, so the routing contract cannot be silently undone the day the platform pin moves.

    Make deploy and rollback mean it when they say a release is live. The wait polled with fetch, which keeps one connection alive — so every poll re-interviewed the single edge machine that answered first, agreed with itself instantly, and printed Deployed! while a quarter of fresh connections still served the previous release for up to 95 seconds. Each probe now opens its own connection and the wait succeeds only after ten independent ones agree. What cannot be verified is said plainly instead of implied: --json carries serving: "confirmed" | "unconfirmed" | "unverifiable", and the human output distinguishes "the edge is still rolling this release out" from "serving could not be verified from here". The old reachability probe is gone — it proved the app answered, never which release did.

    Also: collaborators accept now points at clone, the command that works when you have just been granted access and have no checkout (pull failed with not_in_app_repo); accepting an invite you already accepted reports that you are a member instead of sending you to ask the owner for another billed invite; collaborators add --json no longer prints the invite's /join token into terminals and CI logs; collaborators invites drops the appName field that echoed the app id under a name it did not have; a refused upload carries its numbers into --json (usedBytes, limitBytes) rather than leaving them in prose; and app undeploy refuses with not_app_owner like every other owner-only surface.

    Internally, the routing decisions these fixes depend on now live in one place (shared/app-routing.ts) that the deploy worker, the CLI and the scaffold all read, replacing two hand-maintained copies of the reserved-path list whose divergence nothing would have reported. The scaffold's TOML cannot import a constant, so a test pins it to the same values — that is what keeps deepspace dev routing like production. docs/platform/app-platform-contract.md states the rules this boundary follows, including why a fix moves the platform to match app code rather than the reverse.