Skip to content

feat: improve core to support vercel #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 45 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
22648f9
feat: possibility to override dev env
magne4000 May 28, 2025
2aa7ff2
fix: help vite importer
magne4000 May 28, 2025
7010e73
chore: add photon logo to tests
magne4000 Jun 3, 2025
4c02cd4
refactor: better Error management
magne4000 Jun 3, 2025
14e6ab5
feat: UM entries can be standalone
magne4000 Jun 4, 2025
d0134cf
refactor: virtual apply
magne4000 Jun 4, 2025
52bfe83
feat: apply for a unique handler
magne4000 Jun 4, 2025
f12b1f7
feat: support for server entry with exclusive handler
magne4000 Jun 4, 2025
53eee26
fix: ensure proper resolution of handlers in adapters
magne4000 Jun 4, 2025
8cf747d
fix: early resolve photon meta
magne4000 Jun 4, 2025
33dc8ab
feat: new getPhotonServerEntryId util
magne4000 Jun 4, 2025
8c4f465
fix: dead-lock
magne4000 Jun 4, 2025
39ad723
chore: cleanup
magne4000 Jun 4, 2025
9fc38a1
refactor
magne4000 Jun 4, 2025
065ae75
chore: simplify early meta resolution
magne4000 Jun 4, 2025
8351422
chore: remove direct rollup references + cloudflare deps upgrade
magne4000 Jun 4, 2025
970eb25
chore: lint
magne4000 Jun 4, 2025
2e83f04
fix: bun and deno conditions
magne4000 Jun 10, 2025
4c79c26
test: bun
magne4000 Jun 10, 2025
093266e
feat: add support for dev env emulation per handler
magne4000 Jun 10, 2025
bb913d9
chore: cleanup
magne4000 Jun 10, 2025
1e738e4
fix: upgrade deps
magne4000 Jun 10, 2025
8809dfe
fix: PhotonMeta type
magne4000 Jun 11, 2025
7b2e48f
fix: handle query parameter checks in Photon server entry resolution
magne4000 Jun 12, 2025
d1228ad
feat: support resolving Photon server entry with handler configuration
magne4000 Jun 12, 2025
d7447e7
fix: module resolution
magne4000 Jun 12, 2025
7a3e429
fix: refine Photon server and handler resolution logic
magne4000 Jun 12, 2025
8e70263
feat: introduce `getPhotonMeta` utility for streamlined Photon meta r…
magne4000 Jun 12, 2025
1b7874a
fix: update Cloudflare plugin to use `getPhotonMeta`
magne4000 Jun 12, 2025
ba56522
chore: simplify fallback server entry
magne4000 Jun 12, 2025
94b889d
chore: replace arktype with zod
magne4000 Jun 16, 2025
87e2d23
chore
magne4000 Jun 16, 2025
d764f2f
refactor: centralize mergeable namespace
magne4000 Jun 16, 2025
ce2d17d
chore: add name prop onto Photon entries
magne4000 Jun 16, 2025
6a5750f
chore
magne4000 Jun 16, 2025
c3136c8
chore
magne4000 Jun 16, 2025
98d1a09
feat: add additionalServerConfigs config
magne4000 Jun 18, 2025
f00ba48
chore
magne4000 Jun 18, 2025
ea1bd26
chore
magne4000 Jun 18, 2025
334eb35
feat: new api helpers
magne4000 Jun 18, 2025
f52bc7a
feat: new api helper ensureUniqueEntry
magne4000 Jun 18, 2025
8b823d9
feat: new api helper stripUniqueEntry
magne4000 Jun 18, 2025
2cc5060
feat: new api helper stripUniqueEntry
magne4000 Jun 18, 2025
7835dc8
chore
magne4000 Jun 18, 2025
da2d121
chore: introduce server-with-config entries
magne4000 Jun 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions example/app-cloudflare/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
"@photonjs/core": "workspace:",
"typescript": "^5.8.3",
"vite": "^6.3.5",
"wrangler": "^4.15.1"
"wrangler": "^4.19.1"
},
"dependencies": {
"@universal-middleware/core": "^0.4.7",
"@universal-middleware/core": "^0.4.8",
"awesome-framework": "workspace:",
"hono": "^4.7.8"
}
Expand Down
5 changes: 5 additions & 0 deletions example/app-cloudflare/testRun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ function testRun(cmd: `pnpm run ${string}`) {
expect(await response.text()).toBe('The API Route')
})

test('/standalone', async () => {
const response: Response = await fetch(`${getServerUrl()}/standalone`)
expect(await response.text()).toBe('The /standalone Route')
})

test('/bar', async () => {
const response: Response = await fetch(`${getServerUrl()}/bar`)
expect(await response.text()).toBe('bar')
Expand Down
1 change: 0 additions & 1 deletion example/app-cloudflare/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/// <reference types="@photonjs/core/api" />
/* The Vite plugin cloudflare() will be replaced by this:
import cloudflare from '@photonjs/cloudflare'
*/
Expand Down
2 changes: 1 addition & 1 deletion example/app-hono-cloudflare/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"@photonjs/core": "workspace:",
"typescript": "^5.8.3",
"vite": "^6.3.5",
"wrangler": "^4.15.1"
"wrangler": "^4.19.1"
},
"dependencies": {
"awesome-framework": "workspace:",
Expand Down
8 changes: 2 additions & 6 deletions example/app-hono-cloudflare/server.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
// Will be moved to @photonjs/hono
import { apply, serve } from '@photonjs/core/hono'
import awesomeFramework from 'awesome-framework/universal-middleware'
import { Hono } from 'hono'

async function startServer() {
const app = new Hono()

apply(
app,
// Adds the framework's middlewares
awesomeFramework,
)
// awesomeFramework will automatically be injected by apply
apply(app)

return serve(app)
}
Expand Down
1 change: 0 additions & 1 deletion example/app-hono-cloudflare/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/// <reference types="@photonjs/core/api" />
/* The Vite plugin cloudflare() will be replaced by this:
import cloudflare from '@photonjs/cloudflare'
*/
Expand Down
3 changes: 2 additions & 1 deletion example/awesome-framework/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
"dependencies": {
"@photonjs/core": "workspace:",
"@universal-middleware/core": "^0.4.7",
"@universal-middleware/core": "^0.4.8",
"@universal-middleware/sirv": "^0.1.20"
},
"devDependencies": {
Expand All @@ -21,6 +21,7 @@
"./entry-client": "./dist/entry-client.js",
"./render": "./dist/render.js",
"./vite": "./dist/vite/index.js",
"./standalone": "./dist/photon/entries/standalone.js",
"./universal-middleware": {
"types": "./dist/photon/entries/prod.d.ts",
"development": {
Expand Down
21 changes: 21 additions & 0 deletions example/awesome-framework/src/photon/entries/standalone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { enhance } from '@universal-middleware/core'

const standaloneMiddleware = enhance(
async () => {
return new Response('The /standalone Route', {
status: 200,
headers: {
'Content-Type': 'text/plain',
},
})
},
// enhance() adds meta data (a Universal Middleware in itself is just a Request => Response function)
{
name: 'awesome-framework:standalone',
path: '/standalone',
method: 'GET',
},
)

// This middleware will be used as a standalone handler, which means it is not included by `./universal-middleware` export
export default standaloneMiddleware
4 changes: 2 additions & 2 deletions example/awesome-framework/src/photon/middlewares/logger.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { enhance, MiddlewareOrder } from '@universal-middleware/core'

export const loggerMiddleware = enhance(
(request: Request) => {
console.log('Request:', request.url)
(request: Request, ctx: Universal.Context) => {
console.log('Request:', request.url, 'Context:', ctx)
},
// enhance() adds meta data (a Universal Middleware in itself is just a Request => Response function)
{
Expand Down
7 changes: 7 additions & 0 deletions example/awesome-framework/src/vite/photonPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,12 @@ export function photonPlugin(): Plugin[] {
resolveMiddlewares() {
return 'awesome-framework/universal-middleware'
},
handlers: {
// declare standalone route
standalone: {
id: 'awesome-framework/standalone',
standalone: true,
},
},
})
}
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"========= Build": "",
"build": "pnpm run build:photon && pnpm run build:awesome-framework",
"build:photon": "pnpm --recursive --filter {packages/*} run build",
"build:awesome-framework": "cd example/awesome-framework/ && pnpm run build",
"build:awesome-framework": "pnpm --filter {example/awesome-framework} run build",
"========= Test": "",
"test": "test-e2e",
"========= Formatting": "",
Expand All @@ -22,5 +22,10 @@
"playwright": "^1.52.0",
"prettier": "^3.2.5"
},
"pnpm": {
"overrides": {
"@types/node": "^20.17.32"
}
},
"packageManager": "pnpm@9.15.9"
}
12 changes: 6 additions & 6 deletions packages/adapter-cloudflare/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@
"release:commit": "release-me commit"
},
"dependencies": {
"@cloudflare/vite-plugin": "^1.0.12",
"@cloudflare/vite-plugin": "^1.5.0",
"@photonjs/core": "workspace:^",
"@universal-middleware/cloudflare": "^0.4.8"
"@universal-middleware/cloudflare": "^0.4.9"
},
"devDependencies": {
"@brillout/release-me": "^0.4.3",
"@cloudflare/workers-types": "^4.20250515.0",
"@universal-middleware/core": "^0.4.7",
"@universal-middleware/h3": "^0.4.9",
"@universal-middleware/hono": "^0.4.10",
"@cloudflare/workers-types": "^4.20250604.0",
"@universal-middleware/core": "^0.4.8",
"@universal-middleware/h3": "^0.4.11",
"@universal-middleware/hono": "^0.4.13",
"crossws": "^0.3.5",
"h3": "^1.15.1",
"tsup": "^8.4.0",
Expand Down
9 changes: 4 additions & 5 deletions packages/adapter-cloudflare/src/adapters/dev.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import type { ExportedHandlerFetchHandler } from '@cloudflare/workers-types'
import { createMissingExportError, createMissingApplyError, createIncompatibleServerError } from '../utils/errors.js'

export function asFetch(app: unknown, id: string): ExportedHandlerFetchHandler {
if (!app) {
throw new Error(`[photon] Missing export default in ${JSON.stringify(id)}`)
throw createMissingExportError(id)
}

const server = (app as Record<symbol, string | undefined>)[Symbol.for('photon:server')]

if (!server) {
throw new Error('[photon] { apply } function needs to be called before export')
throw createMissingApplyError()
}

switch (server) {
Expand All @@ -30,7 +31,5 @@ export function asFetch(app: unknown, id: string): ExportedHandlerFetchHandler {
}
}

throw new Error(
`[photon] Clouflare target is not compatible with server "${server}". We recommend using "hono" instead.`,
)
throw createIncompatibleServerError(server)
}
3 changes: 2 additions & 1 deletion packages/adapter-cloudflare/src/adapters/h3.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import type { ExportedHandlerFetchHandler, Response } from '@cloudflare/workers-types'
import type { apply as applyAdapter } from '@universal-middleware/h3'
import { toWebHandler } from 'h3'
import { createMissingDependencyError } from '../utils/errors.js'

async function tryImportCrossws() {
try {
const { default: wsAdapter } = await import('crossws/adapters/cloudflare')
return wsAdapter
} catch (e) {
throw new Error('crossws is not installed', { cause: e })
throw createMissingDependencyError('crossws', e)
}
}

Expand Down
37 changes: 24 additions & 13 deletions packages/adapter-cloudflare/src/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/// <reference types="@photonjs/core/api" />
import { cloudflare as cloudflareVitePlugins, type PluginConfig } from '@cloudflare/vite-plugin'
import { isPhotonMeta } from '@photonjs/core/api'
import { getPhotonMeta } from '@photonjs/core/api'
import { supportedTargetServers } from '@photonjs/core/vite'
import type { Plugin } from 'vite'

Expand All @@ -24,6 +23,21 @@ export function cloudflare(config?: Omit<PluginConfig, 'viteEnvironment'>): Plug
},
},
},
// TODO Should this be generic for all adapters?
{
name: `${moduleId}:prefixer`,
apply: 'build',
enforce: 'post',
configEnvironment(name, config) {
if (config.consumer === 'server' && config.build?.rollupOptions?.input) {
// Ensured by Photon
const input = config.build.rollupOptions.input as Record<string, string>
for (const key of Object.keys(input)) {
input[key] = `${moduleId}:${input[key]}`
}
}
},
},
supportedTargetServers('cloudflare', ['hono', 'h3']),
{
name: `${moduleId}:resolver`,
Expand All @@ -47,22 +61,16 @@ export function cloudflare(config?: Omit<PluginConfig, 'viteEnvironment'>): Plug
async load(id) {
if (!id.startsWith(virtualModuleId)) return
const actualId = id.slice(virtualModuleId.length + 1)

const info = this.getModuleInfo(id)

if (!isPhotonMeta(info?.meta)) {
return this.error(`[photon][cloudflare] ${actualId} is not a Photon entry`)
}

const meta = await getPhotonMeta(this, id)
const isDev = this.environment.config.command === 'serve'

if (info.meta.photon.type === 'server') {
if (meta.type === 'server') {
// `server` usually exists only during build time
if (info.meta.photon.server) {
if (meta.server) {
return {
// language=ts
code: `import serverEntry from ${JSON.stringify(actualId)};
import { asFetch } from "@photonjs/cloudflare/${info.meta.photon.server}";
import { asFetch } from "@photonjs/cloudflare/${meta.server}";

export const fetch = asFetch(serverEntry);
export default { fetch };
Expand All @@ -87,7 +95,7 @@ export default { fetch };
return {
// language=ts
code: `import handler from ${JSON.stringify(actualId)};
import { getRuntime } from "@universal-middleware/cloudflare";
import { getRuntime } from "photon:resolve-from-photon:@universal-middleware/cloudflare";

export const fetch = (request, env, ctx) => {
return handler(request, {}, getRuntime(env, ctx))
Expand All @@ -100,7 +108,10 @@ export default { fetch };

return this.error(`[photon][cloudflare] Unable to load ${actualId}`)
},

sharedDuringBuild: true,
},
// FIXME do not enforce ssr env
...cloudflareVitePlugins({ ...config, viteEnvironment: { name: 'ssr' } }),
]
}
28 changes: 28 additions & 0 deletions packages/adapter-cloudflare/src/utils/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { PhotonBugError, PhotonConfigError, PhotonDependencyError, PhotonUsageError } from '@photonjs/core/errors'

// Utility functions for common error scenarios
export function createMissingExportError(id: string): PhotonUsageError {
return new PhotonUsageError(`Missing export default in ${JSON.stringify(id)}`)
}

export function createMissingApplyError(): PhotonUsageError {
return new PhotonUsageError('{ apply } function needs to be called before export')
}

export function createIncompatibleServerError(server: string): PhotonConfigError {
return new PhotonConfigError(
`Cloudflare target is not compatible with server "${server}". We recommend using "hono" instead.`,
)
}

export function createMissingDependencyError(dependency: string, cause?: unknown): PhotonDependencyError {
return new PhotonDependencyError(
`${dependency} is not installed. Please install ${dependency} to use this functionality with Cloudflare.`,
{ cause },
)
}

export function assert(condition: unknown): asserts condition {
if (condition) return
throw new PhotonBugError()
}
21 changes: 11 additions & 10 deletions packages/photonjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"./vite": "./dist/plugin.js",
"./api": "./dist/api.js",
"./dev": "./dist/dev.js",
"./errors": "./dist/assert.js",
".": "./dist/index.js",
"./virtual": {
"types": "./virtual.d.ts"
Expand Down Expand Up @@ -256,19 +257,20 @@
"dependencies": {
"@brillout/picocolors": "^1.0.26",
"@brillout/vite-plugin-server-entry": "^0.7.5",
"@universal-middleware/cloudflare": "^0.4.9",
"@universal-middleware/compress": "^0.2.30",
"@universal-middleware/core": "^0.4.7",
"@universal-middleware/elysia": "^0.5.1",
"@universal-middleware/express": "^0.4.16",
"@universal-middleware/fastify": "^0.5.18",
"@universal-middleware/h3": "^0.4.10",
"@universal-middleware/hattip": "^0.4.9",
"@universal-middleware/hono": "^0.4.12",
"@universal-middleware/core": "^0.4.8",
"@universal-middleware/elysia": "^0.5.2",
"@universal-middleware/express": "^0.4.17",
"@universal-middleware/fastify": "^0.5.19",
"@universal-middleware/h3": "^0.4.11",
"@universal-middleware/hattip": "^0.4.10",
"@universal-middleware/hono": "^0.4.13",
"@universal-middleware/sirv": "^0.1.20",
"arktype": "^2.1.20",
"estree-walker": "^3.0.3",
"hono": "^4.7.7",
"oxc-transform": "^0.64.0"
"oxc-transform": "^0.64.0",
"zod": "^3.25.64"
},
"peerDependencies": {
"vite": ">=6"
Expand All @@ -291,7 +293,6 @@
"fastify": "^5.3.0",
"h3": "^1.15.1",
"magic-string": "^0.30.17",
"rollup": "4.38.0",
"tsup": "^8.4.0",
"typescript": "^5.8.3",
"unplugin": "^2.3.2",
Expand Down
28 changes: 15 additions & 13 deletions packages/photonjs/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import 'vite'
import './types.js'
import './vite-types.js'

export { isPhotonMeta, type PhotonMeta } from './plugin/utils/entry.js'
export { resolvePhotonConfig } from './validators/coerce.js'
export { setPhotonHandler } from './api/setPhotonEntry.js'

declare module 'vite' {
interface UserConfig {
photon?: Photon.Config
}

interface ResolvedConfig {
photon: Photon.ConfigResolved
}
}
export {
addPhotonHandler,
addPhotonServerConfig,
getPhotonServerIdWithHandler,
} from './api/api.js'
export {
PhotonError,
PhotonBugError,
PhotonUsageError,
PhotonConfigError,
PhotonRuntimeError,
PhotonDependencyError,
} from './utils/assert.js'
export { getPhotonMeta } from './utils/meta.js'
Loading