Repository navigation
chore: move to ViteHub 0.1.0 and let ViteHub own the host glue - #105
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Netlify output on the Nitro RC (vite-hub/vitehub#3173), D1 migrations over HTTP on Wrangler's ledger (#3174), one Wrangler entry per binding (#3175), a per-host Schedule cron (#3177), Nitro's cache on KV (#3178), and detectPreset (#3179). pnpm takes one patch per package version, so vite-hub and @vite-hub/database each carry several.
Netlify uses Nitro's own .netlify/v1/functions output, without Drop's function
wrapper, [functions] table, or catch-all redirect. Vercel's daily cleanup cron is
schedule.cron instead of scripts/vercel-cron.mjs. ViteHub writes one Wrangler
entry per binding, so syncCloudflareTemplate goes. scripts/deployment.ts reads the
host with detectPreset. Vercel and Netlify migrate D1 with vitehub db migrate
(Deno and the VPS image keep scripts/migrate-d1.mjs). Cloudflare's Nitro cache is
cache: { driver: "kv" } instead of a hand mount, with the same keys. The D1
database id over HTTP is a runtime env declaration instead of an all-zero
placeholder.
…its release The publish gate takes comark-linter's own severity and keeps Drop's stricter extras (unclosed blocks, broken attributes, unknown components, annotate rules) instead of a copied list of the linter's error rules. comark-ui's skills come from comark-ui/skills in the installed release instead of a build-time fetch of comark-ui.onmax.me. Cached renders carry comark-ui's version in their integrity, so an upgrade rerenders them without a hand bump.
There was a problem hiding this comment.
Important
Netlify builds become publishable in this PR, but they are missing the cleanup schedule that the application relies on and still documents as part of the host contract.
Reviewed changes across the complete three-commit diff, including the patched ViteHub deployment and migration packages, removed Drop-owned host glue, generated-host configuration, Comark lint/skills integration, cache integrity, and associated tests.
- Host glue ownership: moves Netlify output handling, Vercel cron customization, Cloudflare Wrangler deduplication, preset detection, and Nitro KV caching into patched ViteHub packages.
- D1 migrations: changes Vercel and Netlify builds to
vitehub db migrate, with direct D1 HTTP application through Wrangler'sd1_migrationsledger. - Comark integration: consumes exported lint severity, bundles skills from the installed
comark-uirelease, and includes that release version in Markdown cache integrity. - Deployment configuration: removes the old Netlify wrapper/redirect, Vercel post-build rewrite, and Cloudflare post-compile synchronization while preserving host-specific storage and database choices.
⚠️ Netlify deployments have no cleanup job
The PR makes the previously failing Netlify build complete, but its own verification found that no standalone scheduled function is emitted under .netlify. A deployed Netlify instance therefore never runs the lifecycle work still promised by the host configuration and docs, so this should be fixed before treating Netlify as a supported working target.
Technical details
# Restore Netlify cleanup scheduling
## Affected sites
- `nuxt.config.ts:77-82` — configures Netlify with `schedule: { providerOutput: "standalone" }`, but the generated output contains no cleanup function.
- `server/schedules/cleanup.ts:4-20` — the absent job handles quota reservations, code images, expired drops, public links, OAuth clients, viewer identities, and search indexing backlog.
- `content/docs/self-host/index.md:165-168` — promises hourly Netlify cleanup and explains its retention effects.
- `content/docs/self-host/netlify.md:3,40` — advertises a Netlify scheduled function that is not deployed.
## Required outcome
- A Netlify deployment must include an hourly invocation of the `cleanup` schedule, or Netlify must not be presented as a functioning deployment target until the upstream standalone schedule path supports this Nuxt/Nitro output.
- Add generated-output coverage that fails when the expected Netlify scheduled function or schedule declaration is absent.gpt-5.6-sol | 𝕏
Latest result Pull request merged. Final answersSession Pull request merged. Session Pushed 2c00e13 fixing Knip’s transitive deployment-test imports. `pnpm knip` reproduced the failure and now passes; `pnpm test test/deploy/` passed all 14 tests; focused lint and formatting passed. Prior migration and cleanup findings remain fixed. Host setup failed on the pnpmfile checksum, and automatic prepare hit protected Git config; `pnpm install --frozen-lockfile --ignore-scripts` passed. Awaiting hosted CI. Session Pushed 2c00e13 fixing Knip’s transitive deployment-test imports. `pnpm knip` reproduced the failure and now passes; `pnpm test test/deploy/` passed all 14 tests; focused lint and formatting passed. Prior migration and cleanup findings remain fixed. Host setup failed on the pnpmfile checksum, and automatic prepare hit protected Git config; `pnpm install --frozen-lockfile --ignore-scripts` passed. Awaiting hosted CI. Pushed 2c00e13 fixing Knip’s transitive deployment-test imports. `pnpm knip` reproduced the failure and now passes; `pnpm test test/deploy/` passed all 14 tests; focused lint and formatting passed. Prior migration and cleanup findings remain fixed. Host setup failed on the pnpmfile checksum, and automatic prepare hit protected Git config; `pnpm install --frozen-lockfile --ignore-scripts` passed. Awaiting hosted CI. Session Pushed b32b6d8. Fixed Codex P1 deployment migrations and Pullfrog P1 missing Netlify cleanup; resolved the migration thread. `pnpm test test/deploy/` passed 14 tests; focused lint, formatting, and frozen installation passed. Host dependency refresh still fails because --ignore-pnpmfile conflicts with pnpmfileChecksum. Awaiting hosted checks. Session Pass interrupted; current webhook state retained. Session Pass interrupted; current webhook state retained. Session Waiting for review checks before the next pass. |
01a44ed to
ed30fea
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ed30feafea
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Important
No new issue was introduced by this delta, but the previously reported Netlify cleanup-schedule concern remains unresolved, so this review does not approve the PR.
Reviewed changes since the prior Pullfrog review, covering the Cloudflare template schema adjustment.
- Allowed provisioned D1 IDs to be absent: relaxed
scripts/cloudflare-template.tsso the checked-inwrangler.jsoncD1 binding can omitdatabase_id, consistent with the existing environment and local-build fallback chain.
gpt-5.6-sol | 𝕏
|
The migration repair keeps ViteHub's default safety gate intact and explicitly opts both deployment commands into Drop's checked-in destructive migrations (0004 and 0009), matching the previous deployment runner's behavior. The new D1 HTTP regression first reproduces DATABASE_MIGRATION_DATA_LOSS without that opt-in, then applies each host command twice to a fresh SQLite-backed D1 mock and verifies the complete schema, migration ledger, and foreign keys without spawning Drizzle Kit. The cleanup concern in Pullfrog reviews 5729040923 and 5729107732 is repaired in the existing schedule package patch. Nuxt replays Vite config hooks without running the standalone output hooks, so a Nitro module now bundles schedules with Nitro's aliases and generated service bindings and emits an hourly Netlify function with its own server bundle. The generated-output regression copies only that function's directory and successfully invokes cleanup with HTTP 204, while checking that Nitro's native server function still exists. These are both P1 findings; both regressions failed before the repair and pass in b32b6d8. |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes since the prior Pullfrog review, covering the Netlify schedule restoration and deployment migration opt-in.
- Restored Netlify cleanup scheduling: generated a scheduled function from Nitro's bundled server output so aliases and generated service bindings remain available, with focused coverage that executes the copied deployment artifact independently.
- Allowed intentional deploy migrations: added
--allow-data-lossto the Vercel and Netlify migration commands and documented the destructive schema-removal policy, with coverage for rejection without the flag, successful application, idempotency, and foreign-key integrity.
gpt-5.6-sol | 𝕏

Problem
Drop carried its own host glue: a Netlify function wrapper and catch-all redirect, a script that rewrote Vercel's cron after the build, a hook that deduplicated
.output/server/wrangler.json, its own preset detection, a hand-mounted Nitro cache on Cloudflare, and an all-zero D1 id. On the Nitro RC the Netlify build broke outright: ViteHub looked for the beta'sfunctions-internal/entry and failed with INTERNAL_B0040.ViteHub now has PRs for each of these. Until they ship, this PR carries them as pnpm patches on the published 0.1.0 tarballs and deletes the Drop code they replace.
Moving vite-hub and every
@vite-hub/*package to npm 0.1.0, and dropping the 0.0.4 patches, already landed on main in #63. comark-ui and comark-linter were already on npm 0.8.0 (#103).What changed
pnpm patches (one per package version)
vite-hub@0.1.0.netlify/v1/functions/server/server.mjs, writes no wrappercache: { driver: "kv" }mounts Nitro's cache on the app's KV. The driver lives atvite-hub/_internal/kv-cache, not_internal/kv/cacheas in the PR: Nuxt aliasesvite-hub/_internal/kvto a file, so the PR's path resolved tokv.js/cacheand the Nuxt build failed. The PR needs the same renamedetectPreset()fromvite-hub/deployment//_vitehub/page/..., which made the Vercel build fail withEACCES: mkdir '/_vitehub'@vite-hub/database@0.1.0vitehub db migrateon D1 over HTTP uses Wrangler'sd1_migrationsledger, without Drizzle Kit@vite-hub/schedule@0.1.0schedule: { cron }per host build (the Vercel part only)vitehub-schedule-cleanup)@vite-hub/kv@0.1.0@vite-hub/auth@0.1.0console.roles.readgives the Console a computed role for its labels, Admins filter and impersonation check, without writing the database@vite-hub/realtime@0.1.0blobDocument(store)stores a Realtime document as one Blob file, with the same SHA-256 digests Drop usedcomark-ui@0.8.0shiki: { highlighter, themes, maxBlocks, maxSource }option colors code with the host's own Shiki within a budgetThe Nitro 3.0.261029-rc patch (nitrojs/nitro#4967, merged but unreleased) and the
nitrooverride stay as they were.Host glue removed
netlify/functions/server.mjs, the[functions]table, and the catch-all redirect are gone. Nitro'sserver.mjsdeclarespath: "/*"andpreferStatic, so static files are now served before the function.scripts/vercel-cron.mjsis gone. The Vercel build setsschedule: { cron: { cleanup: "0 0 * * *" } }.syncCloudflareTemplateand itsnitro:inithook are gone.scripts/cloudflare-template.tsonly readscloudflareTemplate(Worker name, D1 fallbacks; the D1 id is optional since fix(deploy): Workers Builds passes again #104). Nitro's cache iscache: { driver: "kv" }instead of thecloudflare-kv-bindingmount. Keys keep thenitro-cache:prefix, so existing entries stay valid.scripts/deployment.tsusesdetectPreset. It keepsDROP_HOST, thevpsname, and Drop's sqlite/d1 choice. ANITRO_PRESETthat conflicts with the host now fails in ViteHub's build (VITE_HUB_R0082), not in Drop.pnpm db:migrate(vitehub db migrate). Deno's predeploy and the VPS image keepscripts/migrate-d1.mjs, because neither has the sources or the CLI at that point. The self-host docs say so.d1()helper declaresdatabaseIdasenv({ source: env.source("CLOUDFLARE_D1_DATABASE_ID") })instead of falling back to00000000-…. ViteHub 0.1.0 types accept it, the generated database registry reads it at runtime, andvitehub db migrateresolves it.comark-ui (non-visual)
severity. Drop keeps its stricter extras: unclosed blocks, malformed attributes, unknown components, and everyannotate/*rule.comark-ui/skillsin the installed release. Drop no longer fetches comark-ui.onmax.me at build time, somodules/comark-skills.tsis gone. The package and the site serve the same five skills. The site follows comark-ui main, so it is one unreleased file ahead (comark-ui/references/remark-to-comark.md, and an edited SKILL.md). Drop now serves the files from the release it renders with.integrityof cached Markdown renders is<comark-ui version>+drop.18, so a comark-ui upgrade rerenders cached docs without a hand bump. Existing renders rerender once on deploy.Verified
pnpm fmt --check,pnpm lint,pnpm knip,pnpm typecheck,pnpm run doctor: pass.pnpm install --frozen-lockfilepasses.pnpm test: 65 files, 428 passed, 3 skipped. On one earlier run under load, two Playwright tests (Marker, embedded video) timed out at 5 s; they passed when rerun.pnpm build(Cloudflare):.output/server/wrangler.jsonhas one entry for each ofd1_databases,kv_namespaces,r2_buckets, and the$DurableObjectbinding. It also has deduplicated flags and secrets andname: vitehub-drop, and leaves out the all-zero KV id. WithCLOUDFLARE_D1_DATABASE_IDset, the one D1 entry carries that id, which is the Workers Builds case fix(deploy): Workers Builds passes again #104 fixed. With the patch, a placeholder id inwrangler.jsoncwould no longer clash either.wrangler dev --config .output/server/wrangler.jsonbooted, and/,/api/me,/docs, and/.well-known/skills/index.jsonanswered 200 (the index lists all five comark-ui skills). A cached handler wrotenitro-cache:nitrohandlers…githubstars…into the local KV.DROP_HOST=netlify pnpm build: completes..netlify/v1/functions/server/server.mjsis there,.netlify/deployment.jsonrecordsentry: v1/functions/server/server.mjs, and there is no.netlify/functions/wrapper.DROP_HOST=vercel pnpm build:.vercel/output/config.jsonhas{ "path": "/api/vitehub/schedules/vercel/cleanup", "schedule": "0 0 * * *" }, andapi/vitehub/schedules/vercel/cleanup.funcis there.DROP_HOST=vercel pnpm db:migrate --dry-runwith fake credentials goes to D1's HTTP/queryendpoint with the database id from the env, and never starts Drizzle Kit.Fixed since the first push
Netlify scheduled cleanup: the schedule patch now emits it through Nitro, and
test/deploy/netlify-schedule.test.mjsruns the function from its own folder. Upstream root cause and fix: fix(database): keep other plugins' Provider Output on hosts without a hosted Database runtime vitehub#3195.Vercel build: with the fix(console): mount the page tools routes below the Nuxt app base without a double slash vitehub#3191 backport,
DROP_HOST=vercel pnpm buildcompletes (14m 44s on a loaded machine), writes_vitehub/page/session.func, and keeps the daily cleanup cron.Migrations: Vercel and Netlify run
pnpm db:migrate --allow-data-loss, because migration0004drops the old comment tables on purpose and the check refuses it on a fresh database.Node hosts:
nitro.traceDeps: ["mdream"]so the VPS build copies mdream's native binary (until feat: add mdream to native packages unjs/nf3#84 lists it). The Dockerfile no longer copies@libsql(Nitro already traces it) or@mdreamby hand. A copied VPS.outputbooted, migrated libsql, and served/d/<link>/t.md..pnpmfile.cjsis gone: on npm comark-ui 0.8.0 pnpm no longer installs extra comark-* copies for the linter's optional peers (3491 packages with or without it). knip andpnpm test(430 passed, 3 skipped) pass.Typed generics: the
getAuthForRequestdouble cast and thehostEvent as H3Eventcasts are gone. ViteHub 0.1.0 already types both (#2682, #2685), so these were leftovers.Console roles:
server/auth.tspassesconsole.roles.read(Drop'sroleOfper account), sosyncConsoleRolesand its full-table sweep on each Console open are gone.syncRolestays: Better Auth's admin endpoints check the signed-in admin's stored role. The two sweep tests became one that checks opening the Console writes no other account's role.Live drafts:
draftDocumentisblobDocument(blob). Same Blob keys and digests, so rooms open during the deploy keep saving. A script against the patched dist confirmed that a write from someone else makes the save throwRealtimeDocumentConflict.Code highlighting: Drop's own
highlight()plugin and the splice before comark-code are gone; the preset'sshikioption uses Drop's shared highlighter withmaxSource: 200_000. Two changes: a short block after the budget runs out can still get colors, and each block over the budget adds a warning (never an error, so publishing isn't refused).test/cost/highlight.test.mjspasses.After these: server
tsc, lint, knip, andpnpm test(429 passed, 3 skipped) pass. No new host build was run for this round.Skipped
deriveSecret: skipped. The sealed values are all short-lived (review tokens 12 h, choices 10 min, upload tickets 15 min, app-file grants 12 h). ButderiveSecrethas no fallback: it throws withoutVITEHUB_SECRET(orNUXT_APP_SECRET). Quiver's Drop has noVITEHUB_SECRET, and self-hosters only need it forDROP_CONTENT_ORIGIN. Switching would break site review, uploads, and app files there, and would turn view counting into an error. This waits for Quiver's secret.route: falseinserver/auth.ts: kept. It dates from the first Nuxt rebuild (b82ecfc), with no reason recorded. Turning it off would mount ViteHub's own/api/auth/**handler and its Vite dev middleware, which answers auth requests before Nitro. Innuxt devthat skips Drop's middlewares on those paths (oauth-register, members-only, proxy sign-out, admin-api).vite-huband@vite-hub/content0.1.0 declarecomark-contentexactly 0.4.1 as a peer.Follow-ups
kv-cacherename above. #3173 does not restore Netlify's schedule function on the RC.0.19.console-pages.ts), #3203 (unavailable services, deletes the#code-image-png/#live-draftsstubs), #3204 (Cloudflare deploy command), #3207 (KVsetIfAbsent; Drop should move upload claims to D1 instead), #3208 (migrate on Node start, deletesserver/database-migrate.ts); fix(diagrams,math,charts): ignore renderer-only attributes a document writes itself onmax/comark-ui#321 (authored SVG, deletesauthored()), #322 (typeset stylesheet, deletes most ofshared/typeset.css), #323 (preset tag lists, deletesALLOWED_TAGS/LINT_COMPONENTScopies), #324 (Mermaid limits, deletes Drop's own Mermaid), #325 (comments CSS, deletesmodules/comark-comments-css.ts). A Blob conditional put would makeblobDocumentatomic; not opened yet.🤖 Generated with Claude Code