Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
80 changes: 40 additions & 40 deletions docs/implementation-docs/2026-03-26-cedarjs-project-overview.md

Large diffs are not rendered by default.

309 changes: 227 additions & 82 deletions docs/implementation-plans/universal-deploy-integration-plan-refined.md

Large diffs are not rendered by default.

1,125 changes: 1,125 additions & 0 deletions docs/implementation-plans/universal-deploy-phase-4-detailed-plan.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/api-server/dist.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('dist', () => {
expect(fs.existsSync(path.join(distPath, '__tests__'))).toEqual(false)
})

it('ships three bins', () => {
it('ships six bins', () => {
expect(packageConfig.bin).toMatchInlineSnapshot(`
{
"cedarjs-api-server-watch": "./dist/watch.js",
Expand Down
16 changes: 16 additions & 0 deletions packages/api-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@
"types": "./dist/cjs/bothCLIConfigHandler.d.ts",
"default": "./dist/cjs/bothCLIConfigHandler.js"
},
"./udDispatcher": {
"import": {
"types": "./dist/udDispatcher.d.ts",
"default": "./dist/udDispatcher.js"
}
},
"./udFetchable": {
"import": {
"types": "./dist/udFetchable.d.ts",
"default": "./dist/udFetchable.js"
}
},
"./watch": {
"import": {
"types": "./dist/watch.d.ts",
Expand Down Expand Up @@ -118,6 +130,8 @@
"@cedarjs/web-server": "workspace:*",
"@fastify/multipart": "9.4.0",
"@fastify/url-data": "6.0.3",
"@universal-deploy/node": "^0.1.6",
"@universal-deploy/store": "^0.2.1",
"ansis": "4.2.0",
"chokidar": "3.6.0",
"dotenv-defaults": "5.0.2",
Expand All @@ -128,7 +142,9 @@
"picoquery": "2.5.0",
"pretty-bytes": "5.6.0",
"pretty-ms": "7.0.1",
"rou3": "^0.8.1",
"split2": "4.2.0",
"srvx": "^0.11.9",
"yargs": "17.7.2"
},
"devDependencies": {
Expand Down
110 changes: 110 additions & 0 deletions packages/api-server/src/__tests__/udFetchable.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { afterEach, describe, expect, it, vi } from 'vitest'

import type { CedarHandler, CedarRequestContext } from '@cedarjs/api/runtime'
import { buildCedarContext } from '@cedarjs/api/runtime'

import { createCedarFetchable } from '../udFetchable.js'

vi.mock('@cedarjs/api/runtime', async (importOriginal) => {
const actual = (await importOriginal()) as any
return {
...actual,
buildCedarContext: vi.fn().mockImplementation(actual.buildCedarContext),
}
})

afterEach(() => {
vi.clearAllMocks()
})

describe('createCedarFetchable', () => {
describe('wraps a CedarHandler', () => {
it('calls buildCedarContext and the handler, and returns the handler Response', async () => {
let capturedCtx: CedarRequestContext | undefined

const handler: CedarHandler = async (_req, ctx) => {
capturedCtx = ctx
return new Response('ok', { status: 200 })
}

const fetchable = createCedarFetchable(handler)
const request = new Request('http://localhost/test')

const response = await fetchable.fetch(request)

expect(buildCedarContext).toHaveBeenCalledWith(request)
expect(capturedCtx).toBeDefined()
expect(response.status).toBe(200)
})

it('returns the Response from the handler', async () => {
const handler: CedarHandler = async () => {
return new Response('hello world', {
status: 201,
headers: { 'x-custom': 'value' },
})
}

const fetchable = createCedarFetchable(handler)
const response = await fetchable.fetch(
new Request('http://localhost/test'),
)

expect(response.status).toBe(201)
expect(response.headers.get('x-custom')).toBe('value')
expect(await response.text()).toBe('hello world')
})
})

describe('passes the correct context to the handler', () => {
it('passes query params from the URL', async () => {
let capturedCtx: CedarRequestContext | undefined

const handler: CedarHandler = async (_req, ctx) => {
capturedCtx = ctx
return new Response('ok')
}

const fetchable = createCedarFetchable(handler)
await fetchable.fetch(
new Request('http://localhost/test?name=cedar&version=1'),
)

expect(capturedCtx?.query.get('name')).toBe('cedar')
expect(capturedCtx?.query.get('version')).toBe('1')
})

it('passes cookies from request headers', async () => {
let capturedCtx: CedarRequestContext | undefined

const handler: CedarHandler = async (_req, ctx) => {
capturedCtx = ctx
return new Response('ok')
}

const fetchable = createCedarFetchable(handler)
await fetchable.fetch(
new Request('http://localhost/test', {
headers: { cookie: 'session=abc123; theme=dark' },
}),
)

expect(capturedCtx?.cookies.get('session')).toBe('abc123')
expect(capturedCtx?.cookies.get('theme')).toBe('dark')
})

it('has empty params by default (no route params injected)', async () => {
let capturedCtx: CedarRequestContext | undefined

const handler: CedarHandler = async (_req, ctx) => {
capturedCtx = ctx
return new Response('ok')
}

const fetchable = createCedarFetchable(handler)
await fetchable.fetch(new Request('http://localhost/test'))

expect(capturedCtx?.params).toEqual({})
})
})
})
Loading
Loading