feat(studio): Switch Vercel deployment from MSW to real server mode#929
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…ith Hono serverless functions - Add api/[...path].ts Vercel serverless catch-all using Hono - Add api/_kernel.ts server-side kernel singleton - Extract broker shim to src/lib/create-broker-shim.ts (DRY) - Update vercel.json to VITE_RUNTIME_MODE=server - Add hono and @objectstack/hono dependencies - Update deployment docs Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
|
@copilot 18:58:15.850 @objectstack/spec:build: ✓ data/TenantDatabaseLifecycle.json |
…atibility @vitejs/plugin-react v6 requires Vite 8+ and fails with ERR_PACKAGE_PATH_NOT_EXPORTED when paired with Vite 7. v5.2.0 explicitly supports Vite 4–8. Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
The build failure was caused by |
|
@copilot 从组分之更新最新代码并解决冲突,测试确认 |
There was a problem hiding this comment.
Pull request overview
Updates ObjectStack Studio’s Vercel deployment to run in server mode (Hono serverless function) instead of MSW-only mode, aligning Studio’s hosted behavior with real API endpoints and sharing broker-shim logic across runtimes.
Changes:
- Switch Studio Vercel config to server mode (
VITE_RUNTIME_MODE=server, same-originVITE_SERVER_URL="") and add SPA rewrite that avoids/api/*. - Add Vercel serverless API entrypoint (
api/[...path].ts) and kernel singleton (api/_kernel.ts) using Hono +@objectstack/hono. - Extract the broker shim into
src/lib/create-broker-shim.tsand update deps (addhono,@objectstack/hono, and align@vitejs/plugin-reactto^5.2.0).
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Adds Hono + adapter deps and updates resolved versions (incl. plugin-react downgrade). |
| content/docs/guides/deployment-vercel.mdx | Updates Vercel deployment guide to recommend Server mode and document Hono setup. |
| apps/studio/vercel.json | Switches Studio build env to server mode and adjusts SPA rewrites to avoid /api/*. |
| apps/studio/src/mocks/createKernel.ts | Reuses extracted broker shim instead of inlining broker logic. |
| apps/studio/src/lib/create-broker-shim.ts | New shared broker shim implementation for MSW + server modes. |
| apps/studio/package.json | Adds hono + @objectstack/hono and adjusts @vitejs/plugin-react version. |
| apps/studio/CHANGELOG.md | Notes the server-mode Vercel deployment changes for the release. |
| apps/studio/api/[...path].ts | New Vercel catch-all serverless function that delegates to the inner Hono app. |
| apps/studio/api/_kernel.ts | New server-side kernel singleton + Hono app factory for Vercel warm invocations. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
| }, | ||
| "rewrites": [ | ||
| { "source": "/(.*)", "destination": "/index.html" } | ||
| { "source": "/((?!api/).*)", "destination": "/index.html" } |
| } | ||
| }, | ||
| "rewrites": [ | ||
| { "source": "/((?!api/).*)", "destination": "/index.html" } |
| // --- Singleton state (persists across warm invocations) --- | ||
| let _kernel: ObjectKernel | null = null; | ||
| let _app: Hono | null = null; | ||
|
|
||
| /** | ||
| * Boot the ObjectStack kernel (one-time cold-start cost). | ||
| */ | ||
| async function bootKernel(): Promise<ObjectKernel> { | ||
| if (_kernel) return _kernel; |
| // Seed data from config | ||
| await seedData(kernel, [studioConfig]); | ||
|
|
||
| _kernel = kernel; | ||
| console.log('[Vercel] Kernel ready.'); | ||
| return kernel; | ||
| } | ||
|
|
||
| /** | ||
| * Seed records defined in app configs into the ObjectQL engine. | ||
| */ | ||
| async function seedData(kernel: ObjectKernel, configs: any[]) { | ||
| const ql = (kernel as any).context?.getService('objectql'); | ||
| if (!ql) return; | ||
|
|
||
| // Reserved namespaces ('base', 'system') bypass FQN transformation — | ||
| // objects in these namespaces keep their short name as-is. | ||
| const RESERVED_NS = new Set(['base', 'system']); | ||
| const toFQN = (name: string, namespace?: string) => { | ||
| if (name.includes('__') || !namespace || RESERVED_NS.has(namespace)) return name; | ||
| return `${namespace}__${name}`; | ||
| }; | ||
|
|
||
| for (const appConfig of configs) { | ||
| const namespace = (appConfig.manifest || appConfig)?.namespace as string | undefined; | ||
|
|
||
| const seedDatasets: any[] = []; | ||
| if (Array.isArray(appConfig.data)) { | ||
| seedDatasets.push(...appConfig.data); | ||
| } | ||
| if (appConfig.manifest && Array.isArray(appConfig.manifest.data)) { | ||
| seedDatasets.push(...appConfig.manifest.data); | ||
| } | ||
|
|
||
| for (const dataset of seedDatasets) { | ||
| if (!dataset.records || !dataset.object) continue; | ||
|
|
||
| const objectFQN = toFQN(dataset.object, namespace); | ||
|
|
||
| // Handle PaginatedResult wrapper — InMemoryDriver may return { value: [...] } | ||
| let existing = await ql.find(objectFQN); | ||
| if (existing && (existing as any).value) existing = (existing as any).value; | ||
|
|
||
| if (!existing || existing.length === 0) { | ||
| console.log(`[Vercel] Seeding ${dataset.records.length} records for ${objectFQN}`); | ||
| for (const record of dataset.records) { | ||
| await ql.insert(objectFQN, record); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** |
| import { ObjectKernel, DriverPlugin, AppPlugin } from '@objectstack/runtime'; | ||
| import { ObjectQLPlugin } from '@objectstack/objectql'; | ||
| import { InMemoryDriver } from '@objectstack/driver-memory'; | ||
| import { createHonoApp } from '@objectstack/hono'; | ||
| import { Hono } from 'hono'; | ||
| import appConfig from '../objectstack.config'; | ||
|
|
||
| export const config = { runtime: 'edge' }; | ||
|
|
||
| const app = new Hono().basePath('/api'); | ||
| let _kernel: ObjectKernel | null = null; | ||
| let _app: Hono | null = null; | ||
|
|
||
| async function bootKernel(): Promise<ObjectKernel> { | ||
| if (_kernel) return _kernel; | ||
| _kernel = new ObjectKernel(); | ||
| await _kernel.use(new ObjectQLPlugin()); | ||
| await _kernel.use(new DriverPlugin(new InMemoryDriver(), 'memory')); | ||
| await _kernel.use(new AppPlugin(appConfig)); |
| const app = new Hono(); | ||
|
|
||
| app.all('/*', async (c) => { | ||
| const inner = await getApp(); | ||
| return inner.fetch(c.req.raw); | ||
| }); | ||
|
|
||
| export default handle(app); |
|
@copilot 从主分支更新最新代码并解决冲突,测试确认 |
Merges latest main which bumps vite from 7.3.1 to 8.0.0 and @vitejs/plugin-react from 5.1.4 to 6.0.1. The previous v5.2.0 downgrade is no longer needed since Vite 8 is now the base. Resolves lockfile conflicts and confirms build + tests pass. Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Merged latest main into the branch and resolved lockfile conflicts. Main brought in Vite 8.0.0 + |
Studio's Vercel deployment ran the entire ObjectStack kernel in-browser via MSW — no real backend. This converts it to server mode using Hono serverless functions.
Server-side API (
api/)api/[...path].ts— Vercel catch-all serverless function, delegates to Hono app viahandle()api/_kernel.ts— Lazy-initialized kernel singleton (survives warm invocations), seeds data on cold start, exposescreateHonoApp({ kernel, prefix: '/api/v1' })Broker shim extraction (DRY)
src/lib/create-broker-shim.ts— Extracted the ~265-line inline broker shim fromcreateKernel.tsinto a shared factory. Both MSW (browser) and Hono (server) modes import it.Configuration
vercel.json—VITE_RUNTIME_MODE=server,VITE_SERVER_URL=""(same-origin), rewrite excludes/api/package.json— Addedhono,@objectstack/honoDocs
deployment-vercel.mdx— server mode is now the default; added Hono+Vite SPA example as Option B✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.