Skip to content

Commit e1815e3

Browse files
Fix merge conflicts and add termi-link dependency
Rebase onto current main and add missing termi-link to api-server dependencies.
1 parent a55c91d commit e1815e3

2 files changed

Lines changed: 133 additions & 17 deletions

File tree

packages/api-server/package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,12 @@
8787
"main": "./dist/createServer.js",
8888
"types": "./dist/createServer.d.ts",
8989
"bin": {
90+
"cedar-api-server-watch": "./dist/cjs/watch.js",
91+
"cedar-log-formatter": "./dist/cjs/logFormatter/bin.js",
92+
"cedar-server": "./dist/cjs/bin.js",
9093
"cedarjs-api-server-watch": "./dist/watch.js",
9194
"cedarjs-log-formatter": "./dist/logFormatter/bin.js",
9295
"cedarjs-server": "./dist/bin.js",
93-
"rw-api-server-watch": "./dist/cjs/watch.js",
9496
"rw-log-formatter": "./dist/cjs/logFormatter/bin.js",
9597
"rw-server": "./dist/cjs/bin.js"
9698
},
@@ -123,7 +125,7 @@
123125
"dotenv-defaults": "5.0.2",
124126
"fast-glob": "3.3.3",
125127
"fast-json-parse": "1.0.3",
126-
"fastify": "5.8.4",
128+
"fastify": "5.8.5",
127129
"fastify-raw-body": "5.0.0",
128130
"picoquery": "2.5.0",
129131
"pretty-bytes": "5.6.0",
@@ -139,7 +141,7 @@
139141
"@types/split2": "4.2.3",
140142
"@types/yargs": "17.0.35",
141143
"graphql-yoga": "5.21.0",
142-
"memfs": "4.57.1",
144+
"memfs": "4.57.2",
143145
"pino-abstract-transport": "1.2.0",
144146
"tsx": "4.21.0",
145147
"typescript": "5.9.3",

packages/api-server/src/plugins/lambdaLoader.ts

Lines changed: 128 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,46 @@ import type {
1515
} from 'fastify'
1616
import { terminalLink } from 'termi-link'
1717

18+
import type {
19+
CedarHandler,
20+
CedarRouteRecord,
21+
LegacyHandler,
22+
} from '@cedarjs/api/runtime'
23+
import { buildCedarContext, wrapLegacyHandler } from '@cedarjs/api/runtime'
1824
import { getPaths } from '@cedarjs/project-config'
1925

2026
import { requestHandler } from '../requestHandlers/awsLambdaFastify.js'
2127
import { escape } from '../utils.js'
2228

23-
export type Lambdas = Record<string, Handler>
24-
export const LAMBDA_FUNCTIONS: Lambdas = {}
29+
export const LAMBDA_FUNCTIONS = new Map<string, Handler>()
30+
export const CEDAR_HANDLERS = new Map<string, CedarHandler>()
31+
const cedarRouteManifest: CedarRouteRecord[] = []
32+
33+
/**
34+
* Exports a copy of the Cedar route manifest.
35+
*
36+
* This is intended to be used to later build WinterTC compatible `fetch`
37+
* exports
38+
*/
39+
export const getCedarRouteManifest = () => [...cedarRouteManifest]
2540

26-
// Import the API functions and add them to the LAMBDA_FUNCTIONS object
41+
// Import the API functions and add them to the LAMBDA_FUNCTIONS map
2742

2843
export const setLambdaFunctions = async (foundFunctions: string[]) => {
2944
const tsImport = Date.now()
3045
console.log(ansis.dim.italic('Importing Server Functions... '))
3146

47+
cedarRouteManifest.length = 0
48+
LAMBDA_FUNCTIONS.clear()
49+
CEDAR_HANDLERS.clear()
50+
3251
const imports = foundFunctions.map(async (fnPath) => {
3352
const ts = Date.now()
3453
const routeName = path.basename(fnPath).replace('.js', '')
54+
const routePath = routeName === 'graphql' ? '/graphql' : `/${routeName}`
3555

3656
const fnImport = await import(pathToFileURL(fnPath).href)
37-
const handler: Handler = (() => {
57+
const handler: Handler | undefined = (() => {
3858
if ('handler' in fnImport) {
3959
// ESModule export of handler - when using
4060
// `export const handler = ...` - most common case
@@ -58,15 +78,60 @@ export const setLambdaFunctions = async (foundFunctions: string[]) => {
5878
return undefined
5979
})()
6080

61-
LAMBDA_FUNCTIONS[routeName] = handler
62-
if (!handler) {
81+
const cedarHandler: CedarHandler | undefined = (() => {
82+
if (
83+
'handleRequest' in fnImport &&
84+
typeof fnImport.handleRequest === 'function'
85+
) {
86+
return fnImport.handleRequest as CedarHandler
87+
}
88+
89+
if (
90+
'default' in fnImport &&
91+
fnImport.default &&
92+
'handleRequest' in fnImport.default &&
93+
typeof fnImport.default.handleRequest === 'function'
94+
) {
95+
return fnImport.default.handleRequest as CedarHandler
96+
}
97+
98+
return undefined
99+
})()
100+
101+
if (handler) {
102+
LAMBDA_FUNCTIONS.set(routeName, handler)
103+
}
104+
105+
if (cedarHandler) {
106+
CEDAR_HANDLERS.set(routeName, cedarHandler)
107+
} else if (handler) {
108+
CEDAR_HANDLERS.set(routeName, wrapLegacyHandler(handler as LegacyHandler))
109+
}
110+
111+
if (!handler && !cedarHandler) {
63112
console.warn(
64113
routeName,
65114
'at',
66115
fnPath,
67-
'does not have a function called handler defined.',
116+
'does not have a function called handler or handleRequest defined.',
68117
)
69118
}
119+
120+
cedarRouteManifest.push({
121+
path: routePath,
122+
methods:
123+
routeName === 'graphql' ? ['GET', 'POST', 'OPTIONS'] : ['GET', 'POST'],
124+
type:
125+
routeName === 'graphql'
126+
? 'graphql'
127+
: routeName === 'health'
128+
? 'health'
129+
: routeName.toLowerCase().includes('auth')
130+
? 'auth'
131+
: 'function',
132+
entry: fnPath,
133+
})
134+
70135
console.log(
71136
terminalLink(ansis.magenta('/' + routeName), 'file://' + fnPath),
72137
ansis.dim.italic(Date.now() - ts + ' ms'),
@@ -135,25 +200,75 @@ interface LambdaHandlerRequest extends RequestGenericInterface {
135200
}
136201

137202
/**
138-
This will take a fastify request
139-
Then convert it to a lambdaEvent, and pass it to the the appropriate handler for the routeName
140-
The LAMBDA_FUNCTIONS lookup has been populated already by this point
141-
**/
203+
* This will take a fastify request
204+
* Then convert it to a lambdaEvent, and pass it to the the appropriate handler
205+
* for the routeName
206+
* The CEDAR_HANDLERS and LAMBDA_FUNCTIONS maps have been populated already by
207+
* this point
208+
*/
142209
export const lambdaRequestHandler = async (
143210
req: FastifyRequest<LambdaHandlerRequest>,
144211
reply: FastifyReply,
145212
) => {
146213
const { routeName } = req.params
214+
const cedarHandlerCandidate = CEDAR_HANDLERS.get(routeName)
215+
const cedarHandler =
216+
typeof cedarHandlerCandidate === 'function'
217+
? cedarHandlerCandidate
218+
: undefined
219+
220+
if (cedarHandler) {
221+
const requestBody =
222+
req.method === 'GET' || req.method === 'HEAD'
223+
? undefined
224+
: typeof req.rawBody === 'string'
225+
? req.rawBody
226+
: req.rawBody
227+
? Buffer.from(req.rawBody).toString()
228+
: undefined
229+
230+
const href = `${req.protocol}://${req.hostname}${req.raw.url ?? '/'}`
231+
const request = new Request(href, {
232+
method: req.method,
233+
headers: req.headers as HeadersInit,
234+
body: requestBody,
235+
})
236+
237+
const ctx = await buildCedarContext(request, {
238+
params: {
239+
routeName,
240+
},
241+
})
242+
243+
const response = await cedarHandler(request, ctx)
244+
245+
reply.status(response.status)
246+
247+
response.headers.forEach((value: string, name: string) => {
248+
reply.header(name, value)
249+
})
250+
251+
const body = await response.arrayBuffer()
252+
reply.send(Buffer.from(body))
253+
254+
return
255+
}
256+
257+
const handler = LAMBDA_FUNCTIONS.get(routeName)
147258

148-
if (!LAMBDA_FUNCTIONS[routeName]) {
259+
if (handler) {
260+
return requestHandler(req, reply, handler)
261+
} else {
149262
const errorMessage = `Function "${routeName}" was not found.`
150263
req.log.error(errorMessage)
151264
reply.status(404)
152265

153266
if (process.env.NODE_ENV === 'development') {
154267
const devError = {
155268
error: errorMessage,
156-
availableFunctions: Object.keys(LAMBDA_FUNCTIONS),
269+
availableFunctions: [
270+
...new Set([...LAMBDA_FUNCTIONS.keys(), ...CEDAR_HANDLERS.keys()]),
271+
],
157272
}
158273
reply.send(devError)
159274
} else {
@@ -162,5 +277,4 @@ export const lambdaRequestHandler = async (
162277

163278
return
164279
}
165-
return requestHandler(req, reply, LAMBDA_FUNCTIONS[routeName])
166280
}

0 commit comments

Comments
 (0)